diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..0c17de6
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+*.umap filter=lfs diff=lfs merge=lfs -text
+*.uasset filter=lfs diff=lfs merge=lfs -text
+*.lib filter=lfs diff=lfs merge=lfs -text
diff --git a/Examples/Config/DefaultEditor.ini b/Examples/Config/DefaultEditor.ini
new file mode 100644
index 0000000..e353d0d
--- /dev/null
+++ b/Examples/Config/DefaultEditor.ini
@@ -0,0 +1,27 @@
+[EditoronlyBP]
+bAllowClassAndBlueprintPinMatching=true
+bReplaceBlueprintWithClass=true
+bDontLoadBlueprintOutsideEditor=true
+bBlueprintIsNotBlueprintType=true
+
+[/Script/Localization.LocalizationSettings]
+-EngineTargetsSettings=(Name="Engine",Guid=33482D004789784C9DA695A682ACCA1B,TargetDependencies=,AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=True,SearchDirectories=((Path="Source/Runtime/"),(Path="Source/Developer/"),(Path="Config/")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*"),(Pattern="Source/Developer/NoRedist/UnrealEngineLauncherServices/*"),(Pattern="Source/Developer/NoRedist/BuildPatchServices/*")),FileExtensions=((Pattern="cpp"),(Pattern="h"),(Pattern="c"),(Pattern="inl"),(Pattern="mm"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=((Pattern="Content/*")),ExcludePathWildcards=((Pattern="Content/Editor/*"),(Pattern="Content/Tutorial/*"),(Pattern="Content/Developers/*"),(Pattern="Content/TestPackages/*"),(Pattern="Content/QA_Assets/*"),(Pattern="Content/Maps/Automation/*"),(Pattern="Content/EngineSounds/*")),FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-EngineTargetsSettings=(Name="Editor",Guid=AC8BFD2A41A2FB2893BB8EA0AF903E6D,TargetDependencies=(33482D004789784C9DA695A682ACCA1B),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=True,SearchDirectories=((Path="Source/Editor/")),ExcludePathWildcards=,FileExtensions=((Pattern="cpp"),(Pattern="h"),(Pattern="c"),(Pattern="inl"),(Pattern="mm"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=((Pattern="Content/Editor/*"),(Pattern="Content/Editor*")),ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-EngineTargetsSettings=(Name="EditorTutorials",Guid=00F8E3AD47F0A73D50D46881C14DF28F,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=("IntroTutorials"),GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=((Pattern="Content/Tutorial/*")),ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-EngineTargetsSettings=(Name="PropertyNames",Guid=E391A8B149980E8154E056AF2DA49479,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="DisplayName"),TextNamespace="UObjectDisplayNames",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-EngineTargetsSettings=(Name="ToolTips",Guid=0F116534468918AEA432DD8C77703BA8,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="ToolTip"),TextNamespace="UObjectToolTips",TextKeyPattern=(Pattern="{FieldPath}")),(MetaDataKey=(Name="ShortToolTip"),TextNamespace="UObjectShortToolTips",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-EngineTargetsSettings=(Name="Keywords",Guid=AE89AECB47475F420D0D69A5547515DC,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="Keywords"),TextNamespace="UObjectKeywords",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-EngineTargetsSettings=(Name="Category",Guid=14B8DEE642A6A7AFEB5A28B959EC373A,TargetDependencies=,AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=False),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="Category"),TextNamespace="UObjectCategory",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="Engine",Guid=33482D004789784C9DA695A682ACCA1B,TargetDependencies=,AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=True,SearchDirectories=((Path="Source/Runtime/"),(Path="Source/Developer/"),(Path="Config/")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*"),(Pattern="Source/Developer/NoRedist/UnrealEngineLauncherServices/*"),(Pattern="Source/Developer/NoRedist/BuildPatchServices/*")),FileExtensions=((Pattern="cpp"),(Pattern="h"),(Pattern="c"),(Pattern="inl"),(Pattern="mm"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=((Pattern="Content/*")),ExcludePathWildcards=((Pattern="Content/Editor/*"),(Pattern="Content/Tutorial/*"),(Pattern="Content/Developers/*"),(Pattern="Content/TestPackages/*"),(Pattern="Content/QA_Assets/*"),(Pattern="Content/Maps/Automation/*"),(Pattern="Content/EngineSounds/*")),FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="Editor",Guid=AC8BFD2A41A2FB2893BB8EA0AF903E6D,TargetDependencies=(33482D004789784C9DA695A682ACCA1B),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=True,SearchDirectories=((Path="Source/Editor/")),ExcludePathWildcards=,FileExtensions=((Pattern="cpp"),(Pattern="h"),(Pattern="c"),(Pattern="inl"),(Pattern="mm"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=((Pattern="Content/Editor/*"),(Pattern="Content/Editor*")),ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="EditorTutorials",Guid=00F8E3AD47F0A73D50D46881C14DF28F,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=("IntroTutorials"),GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=((Pattern="Content/Tutorial/*")),ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="PropertyNames",Guid=E391A8B149980E8154E056AF2DA49479,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="DisplayName"),TextNamespace="UObjectDisplayNames",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="ToolTips",Guid=0F116534468918AEA432DD8C77703BA8,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="ToolTip"),TextNamespace="UObjectToolTips",TextKeyPattern=(Pattern="{FieldPath}")),(MetaDataKey=(Name="ShortToolTip"),TextNamespace="UObjectShortToolTips",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="Keywords",Guid=AE89AECB47475F420D0D69A5547515DC,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=True),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="Keywords"),TextNamespace="UObjectKeywords",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
++EngineTargetsSettings=(Name="Category",Guid=14B8DEE642A6A7AFEB5A28B959EC373A,TargetDependencies=,AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=False),GatherFromMetaData=(IsEnabled=True,IncludePathWildcards=((Pattern="Source/Editor/*"),(Pattern="Source/Runtime/*"),(Pattern="Source/Developer/*")),ExcludePathWildcards=((Pattern="Source/Developer/NoRedist/CommunityPortalServices/*")),KeySpecifications=((MetaDataKey=(Name="Category"),TextNamespace="UObjectCategory",TextKeyPattern=(Pattern="{FieldPath}"))),ShouldGatherFromEditorOnlyData=True),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="en"),(CultureName="es"),(CultureName="ja"),(CultureName="ko"),(CultureName="pt-BR"),(CultureName="zh-CN")))
+-GameTargetsSettings=(Name="Game",Guid=AE0EA34A45461A25BA65A391026F19F8,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=False),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=False),NativeCultureIndex=-1,SupportedCulturesStatistics=((CultureName="en")))
+-GameTargetsSettings=(Name=,Guid=4FDC3D4847696028C05CADAB8D272007,TargetDependencies=,AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=True,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=False),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=False),NativeCultureIndex=-1,SupportedCulturesStatistics=)
++GameTargetsSettings=(Name="Game",Guid=AE0EA34A45461A25BA65A391026F19F8,TargetDependencies=(33482D004789784C9DA695A682ACCA1B,AC8BFD2A41A2FB2893BB8EA0AF903E6D),AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=False,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=False),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=False),NativeCultureIndex=-1,SupportedCulturesStatistics=((CultureName="en")))
++GameTargetsSettings=(Name="NewTarget",Guid=C2E59CDF4A9AFC05C4755F8E61FBFA0D,TargetDependencies=,AdditionalManifestDependencies=,RequiredModuleNames=,GatherFromTextFiles=(IsEnabled=True,SearchDirectories=,ExcludePathWildcards=,FileExtensions=((Pattern="h"),(Pattern="cpp"),(Pattern="ini"))),GatherFromPackages=(IsEnabled=True,IncludePathWildcards=,ExcludePathWildcards=,FileExtensions=((Pattern="umap"),(Pattern="uasset")),ShouldGatherFromEditorOnlyData=False),GatherFromMetaData=(IsEnabled=False,IncludePathWildcards=,ExcludePathWildcards=,KeySpecifications=,ShouldGatherFromEditorOnlyData=False),NativeCultureIndex=0,SupportedCulturesStatistics=((CultureName="ko-KR")))
+
+
diff --git a/Examples/Config/DefaultEngine.ini b/Examples/Config/DefaultEngine.ini
new file mode 100644
index 0000000..b5ec187
--- /dev/null
+++ b/Examples/Config/DefaultEngine.ini
@@ -0,0 +1,56 @@
+[URL]
+[/Script/Engine.UserInterfaceSettings]
+RenderFocusRule=NavigationOnly
+DefaultCursor=None
+TextEditBeamCursor=None
+CrosshairsCursor=None
+GrabHandCursor=None
+GrabHandClosedCursor=None
+SlashedCircleCursor=None
+ApplicationScale=1.000000
+UIScaleRule=ShortestSide
+CustomScalingRuleClass=None
+UIScaleCurve=(EditorCurveData=(PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant,Keys=((Time=480.000000,Value=0.444000),(Time=720.000000,Value=0.666000),(Time=1080.000000,Value=1.000000),(Time=8640.000000,Value=8.000000))),ExternalCurve=None)
+
+[/Script/Engine.RendererSettings]
+r.MobileHDR=True
+r.MobileNumDynamicPointLights=4
+r.MobileDynamicPointLightsUseStaticBranch=True
+r.AllowOcclusionQueries=True
+r.MinScreenRadiusForLights=0.030000
+r.MinScreenRadiusForDepthPrepass=0.030000
+r.PrecomputedVisibilityWarning=False
+r.TextureStreaming=True
+Compat.UseDXT5NormalMaps=False
+r.AllowStaticLighting=True
+r.NormalMapsForStaticLighting=False
+r.GenerateMeshDistanceFields=False
+r.GenerateLandscapeGIData=True
+r.TessellationAdaptivePixelsPerTriangle=48.000000
+r.SeparateTranslucency=True
+r.TranslucentSortPolicy=0
+TranslucentSortAxis=(X=0.000000,Y=-1.000000,Z=0.000000)
+r.CustomDepth=1
+r.DefaultFeature.Bloom=True
+r.DefaultFeature.AmbientOcclusion=True
+r.DefaultFeature.AmbientOcclusionStaticFraction=True
+r.DefaultFeature.AutoExposure=True
+r.DefaultFeature.MotionBlur=True
+r.DefaultFeature.LensFlare=True
+r.DefaultFeature.AntiAliasing=2
+r.EarlyZPass=3
+r.EarlyZPassMovable=False
+r.DBuffer=False
+r.ClearSceneMethod=1
+r.BasePassOutputsVelocity=False
+r.WireframeCullThreshold=5.000000
+UIScaleRule=ShortestSide
+UIScaleCurve=(EditorCurveData=(PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant,Keys=),ExternalCurve=None)
+
+[/Script/HardwareTargeting.HardwareTargetingSettings]
+TargetedHardwareClass=Desktop
+AppliedTargetedHardwareClass=Desktop
+DefaultGraphicsPerformance=Maximum
+AppliedDefaultGraphicsPerformance=Maximum
+
+
diff --git a/Examples/Config/DefaultGame.ini b/Examples/Config/DefaultGame.ini
new file mode 100644
index 0000000..5edb722
--- /dev/null
+++ b/Examples/Config/DefaultGame.ini
@@ -0,0 +1,2 @@
+[/Script/EngineSettings.GeneralProjectSettings]
+ProjectID=6DA75D494D56C84033F23EAA7E92881B
diff --git a/Examples/Config/DefaultInput.ini b/Examples/Config/DefaultInput.ini
new file mode 100644
index 0000000..18cb027
--- /dev/null
+++ b/Examples/Config/DefaultInput.ini
@@ -0,0 +1,62 @@
+
+[/Script/Engine.InputSettings]
+-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
+bAltEnterTogglesFullscreen=True
+bUseMouseForTouch=False
+bEnableMouseSmoothing=True
+bEnableFOVScaling=True
+FOVScale=0.011110
+DoubleClickTime=0.200000
+-ActionMappings=(ActionName="Jump2",Key=Gamepad_FaceButton_Bottom,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+-ActionMappings=(ActionName="Jump2",Key=SpaceBar,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+-ActionMappings=(ActionName="Wave",Key=None,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
++ActionMappings=(ActionName="Jump2",Key=Gamepad_FaceButton_Bottom,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
++ActionMappings=(ActionName="Jump2",Key=SpaceBar,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
++ActionMappings=(ActionName="Wave",Key=Q,bShift=False,bCtrl=False,bAlt=False,bCmd=False)
+-AxisMappings=(AxisName="LookUp",Key=MouseY,Scale=-1.000000)
+-AxisMappings=(AxisName="LookUpRate",Key=Gamepad_RightY,Scale=1.000000)
+-AxisMappings=(AxisName="MoveForward",Key=Down,Scale=-1.000000)
+-AxisMappings=(AxisName="MoveForward",Key=Gamepad_LeftY,Scale=1.000000)
+-AxisMappings=(AxisName="MoveForward",Key=S,Scale=-1.000000)
+-AxisMappings=(AxisName="MoveForward",Key=Up,Scale=1.000000)
+-AxisMappings=(AxisName="MoveForward",Key=W,Scale=1.000000)
+-AxisMappings=(AxisName="MoveRight",Key=A,Scale=-1.000000)
+-AxisMappings=(AxisName="MoveRight",Key=D,Scale=1.000000)
+-AxisMappings=(AxisName="MoveRight",Key=Gamepad_LeftX,Scale=1.000000)
+-AxisMappings=(AxisName="Turn",Key=MouseX,Scale=1.000000)
+-AxisMappings=(AxisName="TurnRate",Key=Gamepad_RightX,Scale=1.000000)
+-AxisMappings=(AxisName="TurnRate",Key=Left,Scale=-1.000000)
+-AxisMappings=(AxisName="TurnRate",Key=Right,Scale=1.000000)
++AxisMappings=(AxisName="LookUp",Key=MouseY,Scale=-1.000000)
++AxisMappings=(AxisName="LookUpRate",Key=Gamepad_RightY,Scale=1.000000)
++AxisMappings=(AxisName="MoveForward",Key=Down,Scale=-1.000000)
++AxisMappings=(AxisName="MoveForward",Key=Gamepad_LeftY,Scale=1.000000)
++AxisMappings=(AxisName="MoveForward",Key=S,Scale=-1.000000)
++AxisMappings=(AxisName="MoveForward",Key=Up,Scale=1.000000)
++AxisMappings=(AxisName="MoveForward",Key=W,Scale=1.000000)
++AxisMappings=(AxisName="MoveRight",Key=A,Scale=-1.000000)
++AxisMappings=(AxisName="MoveRight",Key=D,Scale=1.000000)
++AxisMappings=(AxisName="MoveRight",Key=Gamepad_LeftX,Scale=1.000000)
++AxisMappings=(AxisName="Turn",Key=MouseX,Scale=1.000000)
++AxisMappings=(AxisName="TurnRate",Key=Gamepad_RightX,Scale=1.000000)
++AxisMappings=(AxisName="TurnRate",Key=Left,Scale=-1.000000)
++AxisMappings=(AxisName="TurnRate",Key=Right,Scale=1.000000)
+bAlwaysShowTouchInterface=False
+bShowConsoleOnFourFingerTap=True
+DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
+ConsoleKey=None
+-ConsoleKeys=Tilde
++ConsoleKeys=Tilde
+
+
diff --git a/Examples/Config/Localization/NewTarget_Gather.ini b/Examples/Config/Localization/NewTarget_Gather.ini
new file mode 100644
index 0000000..411991c
--- /dev/null
+++ b/Examples/Config/Localization/NewTarget_Gather.ini
@@ -0,0 +1,36 @@
+[CommonSettings]
+SourcePath=Content/Localization/NewTarget
+DestinationPath=Content/Localization/NewTarget
+ManifestName=NewTarget.manifest
+ArchiveName=NewTarget.archive
+NativeCulture=ko-KR
+CulturesToGenerate=ko-KR
+
+[GatherTextStep0]
+CommandletClass=GatherTextFromSource
+ExcludePathFilters=Config/Localization/*
+FileNameFilters=*.h
+FileNameFilters=*.cpp
+FileNameFilters=*.ini
+
+[GatherTextStep1]
+CommandletClass=GatherTextFromAssets
+ExcludePathFilters=Content/Localization/*
+PackageFileNameFilters=*.umap
+PackageFileNameFilters=*.uasset
+ShouldGatherFromEditorOnlyData=false
+
+[GatherTextStep2]
+CommandletClass=GenerateGatherManifest
+
+[GatherTextStep3]
+CommandletClass=GenerateGatherArchive
+
+[GatherTextStep4]
+CommandletClass=GenerateTextLocalizationReport
+bWordCountReport=true
+WordCountReportName=NewTarget.csv
+bConflictReport=true
+ConflictReportName=NewTarget_Conflicts.txt
+
+
diff --git a/Examples/Content/Color.uasset b/Examples/Content/Color.uasset
new file mode 100644
index 0000000..bc3f057
--- /dev/null
+++ b/Examples/Content/Color.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5a233fcda4b0f4aa646433482c11f199441353bd522a22c3f59cc954a5d3c81f
+size 103296
diff --git a/Examples/Content/ExampleBlueprint.uasset b/Examples/Content/ExampleBlueprint.uasset
new file mode 100644
index 0000000..c891e77
--- /dev/null
+++ b/Examples/Content/ExampleBlueprint.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e14e7d9f9d1b84deef25bf140a8b9548f7bb86351a67a7799c13b6a719944bb
+size 161402
diff --git a/Examples/Content/Geometry/Meshes/1M_Cube.uasset b/Examples/Content/Geometry/Meshes/1M_Cube.uasset
new file mode 100644
index 0000000..5919b81
--- /dev/null
+++ b/Examples/Content/Geometry/Meshes/1M_Cube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:349245bef4ee3ebbf1922aabdaf5a098a7f26f0625d48f3dfcaeb3bb2e650415
+size 74831
diff --git a/Examples/Content/Geometry/Meshes/1M_Cube_Chamfer.uasset b/Examples/Content/Geometry/Meshes/1M_Cube_Chamfer.uasset
new file mode 100644
index 0000000..6d0f85f
--- /dev/null
+++ b/Examples/Content/Geometry/Meshes/1M_Cube_Chamfer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7e1b503aeb4bfb06c23f1291e529b4a3732d3893981bb6882bb60e48dc39ddd9
+size 107373
diff --git a/Examples/Content/Geometry/Meshes/CubeMaterial.uasset b/Examples/Content/Geometry/Meshes/CubeMaterial.uasset
new file mode 100644
index 0000000..2e7a612
--- /dev/null
+++ b/Examples/Content/Geometry/Meshes/CubeMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee28745e598a38998c78a3b8a1206fcc25967fc786f35b273682bcd26a087be6
+size 92524
diff --git a/Examples/Content/Geometry/Meshes/TemplateFloor.uasset b/Examples/Content/Geometry/Meshes/TemplateFloor.uasset
new file mode 100644
index 0000000..8e4baa2
--- /dev/null
+++ b/Examples/Content/Geometry/Meshes/TemplateFloor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:291573ee165d774ea08cf1d8de20ce6513280302f94e5568f7b370c4b394f56c
+size 100624
diff --git a/Examples/Content/Hello2048.umap b/Examples/Content/Hello2048.umap
new file mode 100644
index 0000000..d69de56
--- /dev/null
+++ b/Examples/Content/Hello2048.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7ad05aa9ee71fd01a138138b96a665d96b4a1f0577871568d62e29ce154bff5
+size 629041
diff --git a/Examples/Content/HelloBlueprint.umap b/Examples/Content/HelloBlueprint.umap
new file mode 100644
index 0000000..a6e554c
--- /dev/null
+++ b/Examples/Content/HelloBlueprint.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b315e4969e6d6dcb965e87e38b86e2928312646a0476a8d22e2356e4573b2604
+size 629058
diff --git a/Examples/Content/HelloCanvas.umap b/Examples/Content/HelloCanvas.umap
new file mode 100644
index 0000000..ac193ce
--- /dev/null
+++ b/Examples/Content/HelloCanvas.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:25c3b5a9c03dd292f904b6e78d8c1eaf87e4d7415e0d2e455084a9be44e8962a
+size 7973
diff --git a/Examples/Content/HelloCharacter.umap b/Examples/Content/HelloCharacter.umap
new file mode 100644
index 0000000..39bc396
--- /dev/null
+++ b/Examples/Content/HelloCharacter.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af53de2e12a5af6e1fd7fd78fd6d84f3d3323ff2f46da16c6d01ad5af04eee58
+size 55913
diff --git a/Examples/Content/HelloInputBinding.umap b/Examples/Content/HelloInputBinding.umap
new file mode 100644
index 0000000..ae6c0d0
--- /dev/null
+++ b/Examples/Content/HelloInputBinding.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:648e0c3fc1973c4720228d8bdd4cb0fcc774203646a75178bcd55576cff72f8e
+size 629120
diff --git a/Examples/Content/HelloJade.umap b/Examples/Content/HelloJade.umap
new file mode 100644
index 0000000..c8dbabc
--- /dev/null
+++ b/Examples/Content/HelloJade.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5056b646c4c898aa325d49e94173cd3ce2e4ec382a8e2b3ce245786d708c6eac
+size 7951
diff --git a/Examples/Content/HelloProcess.umap b/Examples/Content/HelloProcess.umap
new file mode 100644
index 0000000..4733d6f
--- /dev/null
+++ b/Examples/Content/HelloProcess.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0ee963278009e1f6a39f73566fdde4576f007437a7be6b23e31588b91277e513
+size 15966
diff --git a/Examples/Content/HelloReplication.umap b/Examples/Content/HelloReplication.umap
new file mode 100644
index 0000000..a814e34
--- /dev/null
+++ b/Examples/Content/HelloReplication.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d933b3baba3ffba6ec5357256233744c029aa49488096e9e0b59aed4a8a3d675
+size 8027
diff --git a/Examples/Content/HelloRest.umap b/Examples/Content/HelloRest.umap
new file mode 100644
index 0000000..c6f6ab1
--- /dev/null
+++ b/Examples/Content/HelloRest.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c35f83efe1358718f2f699321234e1d7b0a4d21372da76be383740f317e3daca
+size 628649
diff --git a/Examples/Content/HelloScope.umap b/Examples/Content/HelloScope.umap
new file mode 100644
index 0000000..53b7665
--- /dev/null
+++ b/Examples/Content/HelloScope.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:931e79135beb85f076204a6b22d269616036fb9f2eb86cda124bc200b6497279
+size 7962
diff --git a/Examples/Content/HelloTimer.umap b/Examples/Content/HelloTimer.umap
new file mode 100644
index 0000000..ccd551a
--- /dev/null
+++ b/Examples/Content/HelloTimer.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:16f2065138d00525083ee7d12ce5ce2e30bb1251c71f3f3573adf1f51bec4310
+size 8036
diff --git a/Examples/Content/HelloUMG.umap b/Examples/Content/HelloUMG.umap
new file mode 100644
index 0000000..f2a0641
--- /dev/null
+++ b/Examples/Content/HelloUMG.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:caebd217129762752f9797a0462693353450cb86faeb09b3a32c8d36740bdb54
+size 7940
diff --git a/Examples/Content/HelloWorld.umap b/Examples/Content/HelloWorld.umap
new file mode 100644
index 0000000..18b116b
--- /dev/null
+++ b/Examples/Content/HelloWorld.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fb7aa3c5f2c4750debc6d673862a84bb01b8986e6955669dfb5567777d040812
+size 628671
diff --git a/Examples/Content/Mannequin/Animations/ThirdPersonIdle.uasset b/Examples/Content/Mannequin/Animations/ThirdPersonIdle.uasset
new file mode 100644
index 0000000..dbb77e1
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPersonIdle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:661b4d295e068c8c4503e0645a2c75ea60077c9ddd51f468483a0d37e87e1164
+size 164075
diff --git a/Examples/Content/Mannequin/Animations/ThirdPersonJump_End.uasset b/Examples/Content/Mannequin/Animations/ThirdPersonJump_End.uasset
new file mode 100644
index 0000000..f4f7b8a
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPersonJump_End.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:535f44b8ade51529bcf126fff531a9bc5bfabf7fb3174752f2257e6b0500b1fc
+size 107950
diff --git a/Examples/Content/Mannequin/Animations/ThirdPersonJump_Loop.uasset b/Examples/Content/Mannequin/Animations/ThirdPersonJump_Loop.uasset
new file mode 100644
index 0000000..ad1aa15
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPersonJump_Loop.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:70a515c03acee981bcb6621785db8706afa190e5fb2562a0fab39d8a0e210246
+size 121710
diff --git a/Examples/Content/Mannequin/Animations/ThirdPersonJump_Start.uasset b/Examples/Content/Mannequin/Animations/ThirdPersonJump_Start.uasset
new file mode 100644
index 0000000..208201a
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPersonJump_Start.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:553f2affb9c62ff55b01f77fb40a4f8a41324187c7d5553b96ec4ea0c3fe5565
+size 116402
diff --git a/Examples/Content/Mannequin/Animations/ThirdPersonRun.uasset b/Examples/Content/Mannequin/Animations/ThirdPersonRun.uasset
new file mode 100644
index 0000000..13526ff
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPersonRun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea7072e21f613d141f41a2ef28696673f0ea9d715d423e49b1f136f6c0dca704
+size 119550
diff --git a/Examples/Content/Mannequin/Animations/ThirdPersonWalk.uasset b/Examples/Content/Mannequin/Animations/ThirdPersonWalk.uasset
new file mode 100644
index 0000000..da2f684
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPersonWalk.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6f9517e938c03660746ddf6367f43c212c8b7ebbd54e34306db0e28b02401e9
+size 132900
diff --git a/Examples/Content/Mannequin/Animations/ThirdPerson_AnimBP.uasset b/Examples/Content/Mannequin/Animations/ThirdPerson_AnimBP.uasset
new file mode 100644
index 0000000..9d13a71
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPerson_AnimBP.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b2b4ce0e70b826541c38b9f532747f1fa54fc7d7dfee0017d08ab74b25e21705
+size 341356
diff --git a/Examples/Content/Mannequin/Animations/ThirdPerson_IdleRun_2D.uasset b/Examples/Content/Mannequin/Animations/ThirdPerson_IdleRun_2D.uasset
new file mode 100644
index 0000000..2e30951
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPerson_IdleRun_2D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b65c26cff2ac80416bb915ddf083e2c1490bfb30ad6cf61b59a5007db539c3de
+size 97656
diff --git a/Examples/Content/Mannequin/Animations/ThirdPerson_Jump.uasset b/Examples/Content/Mannequin/Animations/ThirdPerson_Jump.uasset
new file mode 100644
index 0000000..9c2f6e0
--- /dev/null
+++ b/Examples/Content/Mannequin/Animations/ThirdPerson_Jump.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5892640a409b682c5bc90f1dd7fecb5a29a23be06e5c9c019a7299effbd54575
+size 113266
diff --git a/Examples/Content/Mannequin/Character/Materials/M_UE4Man_Body.uasset b/Examples/Content/Mannequin/Character/Materials/M_UE4Man_Body.uasset
new file mode 100644
index 0000000..2bf9800
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/M_UE4Man_Body.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d2faecca13211801fbbed5d23445516e89c5115897de0cd9ed346832bc0979b
+size 146666
diff --git a/Examples/Content/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset b/Examples/Content/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset
new file mode 100644
index 0000000..be4d475
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7424a787b73c1d4f529dddc027d33ea1d902d48e451496d4534bbb523b7cf2ca
+size 122182
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset
new file mode 100644
index 0000000..f60e7e1
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8b2e6239a56f62edba83a122cfac313e1c108c6945c1eae8e13895541e629b6
+size 88520
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset
new file mode 100644
index 0000000..25e0ba9
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:beb8f7d7b9170fc778263ead685cd5eb2c0585873930f8cf825232db465d753c
+size 100833
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset
new file mode 100644
index 0000000..fc0f7ef
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bebd5af35eae098600e9be59d55033a6aa622d677bd4c20678df5c08f12a8343
+size 85384
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset
new file mode 100644
index 0000000..16ce5fe
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:42b18c5b4817111ea7445b031314ee21b47d9159e94f31356f96b894c47d82c9
+size 89102
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset
new file mode 100644
index 0000000..7a90db2
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:20636a582c2eef0621ebfae0389c1ea2a05f2a135eb9ddccd052c5ee9cb8a73f
+size 456908
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
new file mode 100644
index 0000000..a403edf
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c7f76c15dd847b8ab35a3d4044faea7e3b83f49a47a1ee6337b15687a8caebdc
+size 472485
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset
new file mode 100644
index 0000000..298ab79
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9be54e07b8338e29241e7b2be2192541cf11bab5bcdb51a19e6ff45b51556604
+size 501625
diff --git a/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset
new file mode 100644
index 0000000..3964d38
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9789904cb8ff7c5400a6d85f5c178bfc24a6424bb8e17ca3606df55239c73a89
+size 428303
diff --git a/Examples/Content/Mannequin/Character/Mesh/SK_Mannequin.uasset b/Examples/Content/Mannequin/Character/Mesh/SK_Mannequin.uasset
new file mode 100644
index 0000000..a886b6c
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Mesh/SK_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11d168f5138e49cbff28dcbb4a6afba41e1f288c67d8402f5f93ee4a33c4e98d
+size 3758320
diff --git a/Examples/Content/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset b/Examples/Content/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset
new file mode 100644
index 0000000..7bdc1d1
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fba8d20d98c42c2faead5842d5fe0d7a5d1c9e8a27012e1994a65837d430e673
+size 26016
diff --git a/Examples/Content/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset b/Examples/Content/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset
new file mode 100644
index 0000000..42d6c53
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a35659ac497f71d22d7f8fbe1ae7690577e51fa5d256dc80201ff1fa0de650a3
+size 11121
diff --git a/Examples/Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset b/Examples/Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset
new file mode 100644
index 0000000..d7d3b26
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:22237159c93dbaa8368e0566ad5ecaf09ee72a397c341ee7362d0fbde9868d47
+size 162028
diff --git a/Examples/Content/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset b/Examples/Content/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset
new file mode 100644
index 0000000..beffabb
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:db0d74883f664b04ecb21ab8d6030c9a202519892bead8f64bb1e82947ff9678
+size 95478
diff --git a/Examples/Content/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset b/Examples/Content/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset
new file mode 100644
index 0000000..854c48a
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2f03c8ea8185054b28f3bc161714eb9fb0358cb245d197f6e0bf20e22d95020
+size 224646
diff --git a/Examples/Content/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset b/Examples/Content/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset
new file mode 100644
index 0000000..23a26c4
--- /dev/null
+++ b/Examples/Content/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a8bc9955f2934e00b7979f230f2541001c7ece712535992856361d425b8ad130
+size 5481343
diff --git a/Examples/Content/Scripts/.vscode/launch.json b/Examples/Content/Scripts/.vscode/launch.json
new file mode 100644
index 0000000..d89f033
--- /dev/null
+++ b/Examples/Content/Scripts/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+	"version": "0.1.0",	
+	"configurations": [		
+		{
+			"name": "Attach",
+			"type": "node",
+			// TCP/IP address. Default is "localhost".
+			"address": "localhost",
+			// Port to attach to.
+			"port": 5858,
+			"sourceMaps": false
+		}
+	]
+}
diff --git a/Examples/Content/Scripts/2048/game/actuator.js b/Examples/Content/Scripts/2048/game/actuator.js
new file mode 100644
index 0000000..fce6726
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/actuator.js
@@ -0,0 +1,325 @@
+(function () {
+    "use strict"
+
+    module.exports = function (vbox, data) {
+        var design = require('views/hello2048_design')
+
+        var instantiate = require('instantiator')
+
+        var widget = instantiate(design)
+
+        var score_attrs = widget.find('score').set_attrs
+        var best_attrs = widget.find('best').set_attrs
+        var message_attrs = widget.find('message').set_attrs
+
+        score_attrs({ Text: 0 })
+        best_attrs({ Text: 0 })
+        message_attrs({ Text: ':)' })
+
+        vbox.AddChild(widget)
+
+        function Actuator() {
+            this.scoreContainer = function (text) { score_attrs({ Text: text }) }
+            this.bestContainer = function (text) { best_attrs({ Text: text }) }
+            this.messageContainer = function (text) { message_attrs({ Text: text }) }
+
+            this.score = 0;
+        }
+
+        var tiles = []
+
+        var mesh = data.Block
+        var color_material = Material.Load('/Game/Color.Color')
+
+        function color_from_hex(hex) {
+            var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
+            return result ? {
+                R: parseInt(result[1], 16),
+                G: parseInt(result[2], 16),
+                B: parseInt(result[3], 16)
+            } : null
+        }
+
+        var tile_color = color_from_hex("#eee4da")
+        var tile_gold_color = color_from_hex("#edc22e")
+
+        var text_color = color_from_hex("#222222")
+        var bright_text_color = color_from_hex("#f9f6f2")
+
+        var special_colors = [
+          [false, false], // 2
+          [false, false], // 4
+          ['#f78e48', true], // 8
+          ['#fc5e2e', true], // 16
+          ['#ff3333', true], // 32
+          ['#ff0000', true], // 64
+          [false, true], // 128
+          [false, true], // 256
+          [false, true], // 512
+          [false, true], // 1024
+          [false, true] // 2048
+        ]
+
+        function lerp_scalar(a, b, u) {
+            return (b - a) * u + a
+        }
+
+        function lerp(a, b, u) {
+            if (typeof a == 'object') {
+                var out = {}
+                for (var k in a) {
+                    out[k] = lerp_scalar(a[k], b[k], u)
+                }
+                return out
+            }
+            else {
+                return lerp_scalar(a, b, u)
+            }
+        }
+
+        function log2(x) {
+            return Math.log(x) / Math.log(2)
+        }
+
+        function gamma(x) {
+            var y = {}
+            for (var k in x) {
+                y[k] = Math.pow(x[k] / 255.0, 2.2)
+            }
+            return y
+        }
+
+        function create_tile() {
+            var actor = new StaticMeshActor(GWorld)
+            var material = KismetMaterialLibrary.CreateDynamicMaterialInstance(GWorld, color_material)
+            var smc = actor.StaticMeshComponent
+            smc.SetMobile()
+            smc.SetStaticMesh(mesh)
+            smc.SetMaterial(0, material)
+
+            var desired_pos = null
+            var cur_pos = null
+            var desired_scale = null
+            var cur_scale = null
+            var pos = null
+            var updating = false
+            var counter = 0
+            var max_counter = 0.2
+            var spacing = data.Spacing
+
+            var textactor = new TextRenderActor(GWorld, { Z: 100 })
+            var tile = {
+                set_value: function (value) {
+                    var exponent = log2(value)
+                    var limit = 11
+                    var gold_ratio = (exponent - 1) / (limit - 1)
+                    var mixed_bg = lerp(tile_color, tile_gold_color, gold_ratio)
+                    var special_color = special_colors[exponent - 1]
+                    var special_bg = special_color[0]
+                    var is_bright = special_color[1]
+                    if (special_bg) {
+                        mixed_bg = lerp(mixed_bg, color_from_hex(special_bg), 0.55)
+                    }
+                    var tr = textactor.TextRender;
+                    tr.HorizontalAlignment = 'EHTA_Center'
+                    tr.VerticalAlignment = 'EVRTA_TextCenter'
+                    tr.SetTextRenderColor(is_bright ? bright_text_color : text_color)
+                    tr.XScale = tr.YScale = 2
+                    this.set_color(mixed_bg)
+                    tr.SetText(value)
+                },
+                set_position: function (pos) {
+                    if (cur_pos == null) {
+                        cur_pos = pos
+                        this.sync_position(pos)
+                    }
+                    else {
+                        desired_pos = pos
+                        this.kick()
+                    }
+                },
+                set_scale: function (scale) {
+                    if (cur_scale == null) {
+                        cur_scale = scale
+                        this.sync_scale(scale)
+                    }
+                    else {
+                        desired_scale = scale
+                        this.kick()
+                    }
+                },
+                kick: function (pos) {
+                    counter = $time
+                    if (!updating) {
+                        updating = true
+                        var self = this
+                        function kick() {
+                            window.requestAnimationFrame(function () {
+                                var alpha = Math.min(1, ($time - counter) / max_counter)
+
+                                if (desired_pos != null) {
+                                    var new_pos = lerp(cur_pos, desired_pos, alpha)
+                                    self.sync_position(new_pos)
+                                    cur_pos = new_pos
+                                }
+
+                                if (desired_scale != null) {
+                                    var new_scale = lerp(cur_scale, desired_scale, alpha)
+                                    self.sync_scale(new_scale)
+                                    cur_scale = new_scale
+                                }
+
+                                if (alpha == 1) {
+                                    updating = false
+                                } else {
+                                    kick()
+                                }
+                            })
+                        }
+                        kick()
+                    }
+                },
+                sync_position: function (pos) {
+                    if (!actor) return
+
+                    var l = { X: 0, Y: pos.x * spacing, Z: pos.y * spacing + 100 }                    
+                    actor.SetActorLocation(l)
+                    l.X = l.X + 60
+                    textactor.SetActorLocation(l)
+                },
+                sync_scale: function (scale) {
+                    if (!actor) return
+
+                    scale = Math.max(1e-6,scale)
+                    var s3d = { X: 1, Y: scale, Z: scale }
+                    actor.SetActorScale3D(s3d)
+                    textactor.SetActorScale3D(s3d)
+                },
+                set_color: function (color) {
+                    material.SetVectorParameterValue('Color', gamma(color))
+                },
+                destroy: function () {
+                    textactor.DestroyActor()
+                    actor.DestroyActor()
+                    actor = null
+                }
+            }
+            tiles.push(tile)
+            return tile
+        }
+
+        function destroy_tiles() {
+            for (var x in tiles) {
+                var tile = tiles[x]
+                tile.destroy()
+            }
+            tiles = []
+        }
+
+        Actuator.prototype.destroy = function () {
+            destroy_tiles()
+            widget.RemoveFromParent()
+        }
+
+        Actuator.prototype.actuate = function (grid, metadata) {
+            var self = this;
+
+            window.requestAnimationFrame(function () {
+                destroy_tiles()
+
+                grid.cells.forEach(function (column) {
+                    column.forEach(function (cell) {
+                        if (cell) {
+                            self.addTile(cell);
+                        }
+                    });
+                });
+
+                self.updateScore(metadata.score);
+                self.updateBestScore(metadata.bestScore);
+
+                if (metadata.terminated) {
+                    if (metadata.over) {
+                        self.message(false); // You lose
+                    } else if (metadata.won) {
+                        self.message(true); // You win!
+                    }
+                }
+
+            });
+        };
+
+        // Continues the game (both restart and keep playing)
+        Actuator.prototype.continueGame = function () {
+            this.clearMessage();
+        };
+
+        Actuator.prototype.clearContainer = function (container) {
+            // while (container.firstChild) {
+            //   container.removeChild(container.firstChild);
+            // }
+        };
+
+        Actuator.prototype.addTile = function (tile) {
+            var self = this;
+
+            var wrapper = create_tile()
+            var new_pos = { x: tile.x, y: tile.y }
+            var position = tile.previousPosition || new_pos;
+            wrapper.set_value(tile.value)
+            wrapper.set_position(position)
+
+            if (tile.previousPosition) {
+                window.requestAnimationFrame(function () {
+                    wrapper.set_position(new_pos)
+                })
+            } else if (tile.mergedFrom) {
+                wrapper.set_scale(1.2)
+                wrapper.set_scale(1)
+            } else {
+                wrapper.set_scale(0)
+                wrapper.set_scale(1)
+            }
+        };
+
+        Actuator.prototype.updateScore = function (score) {
+            //this.clearContainer(this.scoreContainer);
+
+            var difference = score - this.score;
+            this.score = score;
+
+            this.scoreContainer(this.score);
+
+            if (difference > 0) {
+                // var addition = document.createElement("div");
+                // addition.classList.add("score-addition");
+                // addition.textContent = "+" + difference;
+
+                // this.scoreContainer.appendChild(addition);
+            }
+        };
+
+        Actuator.prototype.updateBestScore = function (bestScore) {
+            this.bestContainer(bestScore);
+        };
+
+        Actuator.prototype.message = function (won) {
+            var type = won ? "game-won" : "game-over";
+            var message = won ? "You win!" : "Game over!";
+
+            this.messageContainer(message)
+
+            // this.messageContainer.classList.add(type);
+            // this.messageContainer.getElementsByTagName("p")[0].textContent = message;
+        };
+
+        Actuator.prototype.clearMessage = function () {
+            // IE only takes one value to remove at a time.
+            // this.messageContainer.classList.remove("game-won");
+            // this.messageContainer.classList.remove("game-over");
+        };
+
+        return Actuator
+    }
+
+})()
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/game/animframe_polyfill.js b/Examples/Content/Scripts/2048/game/animframe_polyfill.js
new file mode 100644
index 0000000..590bddb
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/animframe_polyfill.js
@@ -0,0 +1,17 @@
+(function (global) {    
+    (function () {
+        var lastTime = 0;
+
+        if (!global.window) global.window = {}
+
+        global.window.requestAnimationFrame = function (callback) {
+            var currTime = $time * 1000;
+            var id = process.nextTick(function () {
+                callback($time);
+            });
+            return id;
+        };
+    }());
+
+    module.exports = {}
+})(this)
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/game/application.js b/Examples/Content/Scripts/2048/game/application.js
new file mode 100644
index 0000000..f698ef0
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/application.js
@@ -0,0 +1,17 @@
+(function () {
+    "use strict"
+
+    module.exports = function (widget, container, data) {
+        require('animframe_polyfill')
+        var Actuator = require('actuator')(container, data)
+        var LocalStorageManager = require('local_storage_manager')
+        var KeyboardInputManager = require('keyboard_input_manager')(widget, container)
+        var GameManager = require('game_manager')
+
+        var game = new GameManager(data.Size, KeyboardInputManager, Actuator, LocalStorageManager)
+
+        return function () {
+            game.destroy()
+        }
+    }
+})()
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/game/game_manager.js b/Examples/Content/Scripts/2048/game/game_manager.js
new file mode 100644
index 0000000..49c6783
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/game_manager.js
@@ -0,0 +1,282 @@
+var Tile = require('tile')
+var Grid = require('grid')
+
+function GameManager(size, InputManager, Actuator, StorageManager) {  
+  this.size           = size; // Size of the grid
+  this.inputManager   = new InputManager;
+  this.storageManager = new StorageManager;
+  this.actuator       = new Actuator;
+
+  this.startTiles     = 2;
+
+  this.inputManager.on("move", this.move.bind(this));
+  this.inputManager.on("restart", this.restart.bind(this));
+  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
+
+  this.setup();
+}
+
+GameManager.prototype.destroy = function () {
+  this.actuator.destroy()
+  this.inputManager.destroy()
+}
+
+// Restart the game
+GameManager.prototype.restart = function () {
+  this.storageManager.clearGameState();
+  this.actuator.continueGame(); // Clear the game won/lost message
+  this.setup();
+};
+
+// Keep playing after winning (allows going over 2048)
+GameManager.prototype.keepPlaying = function () {
+  this.keepPlaying = true;
+  this.actuator.continueGame(); // Clear the game won/lost message
+};
+
+// Return true if the game is lost, or has won and the user hasn't kept playing
+GameManager.prototype.isGameTerminated = function () {
+  return this.over || (this.won && !this.keepPlaying);
+};
+
+// Set up the game
+GameManager.prototype.setup = function () {
+  var previousState = this.storageManager.getGameState();
+
+  // Reload the game from a previous game if present
+  if (previousState) {
+    this.grid        = new Grid(previousState.grid.size,
+                                previousState.grid.cells); // Reload grid
+    this.score       = previousState.score;
+    this.over        = previousState.over;
+    this.won         = previousState.won;
+    this.keepPlaying = previousState.keepPlaying;
+  } else {
+    this.grid        = new Grid(this.size);
+    this.score       = 0;
+    this.over        = false;
+    this.won         = false;
+    this.keepPlaying = false;
+
+    // Add the initial tiles
+    this.addStartTiles();
+  }
+
+  // Update the actuator
+  this.actuate();
+};
+
+// Set up the initial tiles to start the game with
+GameManager.prototype.addStartTiles = function () {
+  for (var i = 0; i < this.startTiles; i++) {
+    this.addRandomTile();
+  }
+};
+
+// Adds a tile in a random position
+GameManager.prototype.addRandomTile = function () {
+  if (this.grid.cellsAvailable()) {
+    var value = Math.random() < 0.9 ? 2 : 4;
+    var tile = new Tile(this.grid.randomAvailableCell(), value);
+
+    this.grid.insertTile(tile);
+  }
+};
+
+// Sends the updated grid to the actuator
+GameManager.prototype.actuate = function () {
+  if (this.storageManager.getBestScore() < this.score) {
+    this.storageManager.setBestScore(this.score);
+  }
+
+  // Clear the state when the game is over (game over only, not win)
+  if (this.over) {
+    this.storageManager.clearGameState();
+  } else {
+    this.storageManager.setGameState(this.serialize());
+  }
+
+  this.actuator.actuate(this.grid, {
+    score:      this.score,
+    over:       this.over,
+    won:        this.won,
+    bestScore:  this.storageManager.getBestScore(),
+    terminated: this.isGameTerminated()
+  });
+
+};
+
+// Represent the current game as an object
+GameManager.prototype.serialize = function () {
+  return {
+    grid:        this.grid.serialize(),
+    score:       this.score,
+    over:        this.over,
+    won:         this.won,
+    keepPlaying: this.keepPlaying
+  };
+};
+
+// Save all tile positions and remove merger info
+GameManager.prototype.prepareTiles = function () {
+  this.grid.eachCell(function (x, y, tile) {
+    if (tile) {
+      tile.mergedFrom = null;
+      tile.savePosition();
+    }
+  });
+};
+
+// Move a tile and its representation
+GameManager.prototype.moveTile = function (tile, cell) {
+  this.grid.cells[tile.x][tile.y] = null;
+  this.grid.cells[cell.x][cell.y] = tile;
+  tile.updatePosition(cell);
+};
+
+// Move tiles on the grid in the specified direction
+GameManager.prototype.move = function (direction) {
+  // 0: up, 1: right, 2: down, 3: left
+  var self = this;
+
+  if (this.isGameTerminated()) return; // Don't do anything if the game's over
+
+  var cell, tile;
+
+  var vector     = this.getVector(direction);
+  var traversals = this.buildTraversals(vector);
+  var moved      = false;
+
+  // Save the current tile positions and remove merger information
+  this.prepareTiles();
+
+  // Traverse the grid in the right direction and move tiles
+  traversals.x.forEach(function (x) {
+    traversals.y.forEach(function (y) {
+      cell = { x: x, y: y };
+      tile = self.grid.cellContent(cell);
+
+      if (tile) {
+        var positions = self.findFarthestPosition(cell, vector);
+        var next      = self.grid.cellContent(positions.next);
+
+        // Only one merger per row traversal?
+        if (next && next.value === tile.value && !next.mergedFrom) {
+          var merged = new Tile(positions.next, tile.value * 2);
+          merged.mergedFrom = [tile, next];
+
+          self.grid.insertTile(merged);
+          self.grid.removeTile(tile);
+
+          // Converge the two tiles' positions
+          tile.updatePosition(positions.next);
+
+          // Update the score
+          self.score += merged.value;
+
+          // The mighty 2048 tile
+          if (merged.value === 2048) self.won = true;
+        } else {
+          self.moveTile(tile, positions.farthest);
+        }
+
+        if (!self.positionsEqual(cell, tile)) {
+          moved = true; // The tile moved from its original cell!
+        }
+      }
+    });
+  });
+
+  if (moved) {
+    this.addRandomTile();
+
+    if (!this.movesAvailable()) {
+      this.over = true; // Game over!
+    }
+
+    this.actuate();
+  }
+};
+
+// Get the vector representing the chosen direction
+GameManager.prototype.getVector = function (direction) {
+  // Vectors representing tile movement
+  var map = {
+    0: { x: 0,  y: -1 }, // Up
+    1: { x: 1,  y: 0 },  // Right
+    2: { x: 0,  y: 1 },  // Down
+    3: { x: -1, y: 0 }   // Left
+  };
+
+  return map[direction];
+};
+
+// Build a list of positions to traverse in the right order
+GameManager.prototype.buildTraversals = function (vector) {
+  var traversals = { x: [], y: [] };
+
+  for (var pos = 0; pos < this.size; pos++) {
+    traversals.x.push(pos);
+    traversals.y.push(pos);
+  }
+
+  // Always traverse from the farthest cell in the chosen direction
+  if (vector.x === 1) traversals.x = traversals.x.reverse();
+  if (vector.y === 1) traversals.y = traversals.y.reverse();
+
+  return traversals;
+};
+
+GameManager.prototype.findFarthestPosition = function (cell, vector) {
+  var previous;
+
+  // Progress towards the vector direction until an obstacle is found
+  do {
+    previous = cell;
+    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
+  } while (this.grid.withinBounds(cell) &&
+           this.grid.cellAvailable(cell));
+
+  return {
+    farthest: previous,
+    next: cell // Used to check if a merge is required
+  };
+};
+
+GameManager.prototype.movesAvailable = function () {
+  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
+};
+
+// Check for available matches between tiles (more expensive check)
+GameManager.prototype.tileMatchesAvailable = function () {
+  var self = this;
+
+  var tile;
+
+  for (var x = 0; x < this.size; x++) {
+    for (var y = 0; y < this.size; y++) {
+      tile = this.grid.cellContent({ x: x, y: y });
+
+      if (tile) {
+        for (var direction = 0; direction < 4; direction++) {
+          var vector = self.getVector(direction);
+          var cell   = { x: x + vector.x, y: y + vector.y };
+
+          var other  = self.grid.cellContent(cell);
+
+          if (other && other.value === tile.value) {
+            return true; // These two tiles can be merged
+          }
+        }
+      }
+    }
+  }
+
+  return false;
+};
+
+GameManager.prototype.positionsEqual = function (first, second) {
+  return first.x === second.x && first.y === second.y;
+};
+
+module.exports = GameManager
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/game/grid.js b/Examples/Content/Scripts/2048/game/grid.js
new file mode 100644
index 0000000..6b218af
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/grid.js
@@ -0,0 +1,121 @@
+var Tile = require('game/tile')
+
+function Grid(size, previousState) {
+  this.size = size;
+  this.cells = previousState ? this.fromState(previousState) : this.empty();
+}
+
+// Build a grid of the specified size
+Grid.prototype.empty = function () {
+  var cells = [];
+
+  for (var x = 0; x < this.size; x++) {
+    var row = cells[x] = [];
+
+    for (var y = 0; y < this.size; y++) {
+      row.push(null);
+    }
+  }
+
+  return cells;
+};
+
+Grid.prototype.fromState = function (state) {
+  var cells = [];
+
+  for (var x = 0; x < this.size; x++) {
+    var row = cells[x] = [];
+
+    for (var y = 0; y < this.size; y++) {
+      var tile = state[x][y];
+      row.push(tile ? new Tile(tile.position, tile.value) : null);
+    }
+  }
+
+  return cells;
+};
+
+// Find the first available random position
+Grid.prototype.randomAvailableCell = function () {
+  var cells = this.availableCells();
+
+  if (cells.length) {
+    return cells[Math.floor(Math.random() * cells.length)];
+  }
+};
+
+Grid.prototype.availableCells = function () {
+  var cells = [];
+
+  this.eachCell(function (x, y, tile) {
+    if (!tile) {
+      cells.push({ x: x, y: y });
+    }
+  });
+
+  return cells;
+};
+
+// Call callback for every cell
+Grid.prototype.eachCell = function (callback) {
+  for (var x = 0; x < this.size; x++) {
+    for (var y = 0; y < this.size; y++) {
+      callback(x, y, this.cells[x][y]);
+    }
+  }
+};
+
+// Check if there are any cells available
+Grid.prototype.cellsAvailable = function () {
+  return !!this.availableCells().length;
+};
+
+// Check if the specified cell is taken
+Grid.prototype.cellAvailable = function (cell) {
+  return !this.cellOccupied(cell);
+};
+
+Grid.prototype.cellOccupied = function (cell) {
+  return !!this.cellContent(cell);
+};
+
+Grid.prototype.cellContent = function (cell) {
+  if (this.withinBounds(cell)) {
+    return this.cells[cell.x][cell.y];
+  } else {
+    return null;
+  }
+};
+
+// Inserts a tile at its position
+Grid.prototype.insertTile = function (tile) {
+  this.cells[tile.x][tile.y] = tile;
+};
+
+Grid.prototype.removeTile = function (tile) {
+  this.cells[tile.x][tile.y] = null;
+};
+
+Grid.prototype.withinBounds = function (position) {
+  return position.x >= 0 && position.x < this.size &&
+         position.y >= 0 && position.y < this.size;
+};
+
+Grid.prototype.serialize = function () {
+  var cellState = [];
+
+  for (var x = 0; x < this.size; x++) {
+    var row = cellState[x] = [];
+
+    for (var y = 0; y < this.size; y++) {
+      row.push(this.cells[x][y] ? this.cells[x][y].serialize() : null);
+    }
+  }
+
+  return {
+    size: this.size,
+    cells: cellState
+  };
+};
+
+module.exports = Grid
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/game/keyboard_input_manager.js b/Examples/Content/Scripts/2048/game/keyboard_input_manager.js
new file mode 100644
index 0000000..bc47a42
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/keyboard_input_manager.js
@@ -0,0 +1,99 @@
+module.exports = function (widget,vbox) {
+  "use strict";
+    
+  var instantiate = require('instantiator')
+
+  function test() {
+    var design = {
+      id : 'button',
+      type : Button,
+      children : [
+        {          
+          type : TextBlock,
+          attrs : {
+            Text : 'Reset'
+          }
+        }
+      ]
+    }
+
+    var widget = instantiate(design)    
+    vbox.AddChild(widget)
+    return widget
+  }    
+
+  var layout = test()
+  var reset_button = Button.C(layout.find('button'))
+  
+  function KeyboardInputManager() {
+    this.events = {};
+
+    this.listen();
+  }
+
+  KeyboardInputManager.prototype.destroy = function () {
+    reset_button.RemoveFromParent()
+  }
+
+  KeyboardInputManager.prototype.on = function (event, callback) {    
+    if (!this.events[event]) {
+      this.events[event] = [];
+    }
+    this.events[event].push(callback);
+  };
+
+  KeyboardInputManager.prototype.emit = function (event, data) {    
+    var callbacks = this.events[event];
+    if (callbacks) {
+      callbacks.forEach(function (callback) {
+        callback(data);
+      });
+    }
+  };    
+
+  KeyboardInputManager.prototype.listen = function () {
+    var map = {
+      'W' : () => this.emit("move", 2),
+      'A' : () => this.emit("move", 1),
+      'S' : () => this.emit("move", 0),
+      'D': () => this.emit("move", 3),
+      'Up': () => this.emit("move", 2),
+      'Left': () => this.emit("move", 1),
+      'Down': () => this.emit("move", 0),
+      'Right': () => this.emit("move", 3)
+    }
+        
+    UserWidget.C(widget.proxy).OnKeyDown = (geom,keyevent) => {
+      var key = KismetInputLibrary.prototype.GetKey(keyevent).KeyName
+
+      var op = map[key]
+      if (op != undefined) {
+        op()
+        return WidgetBlueprintLibrary.Handled()
+      }
+      else {
+        return WidgetBlueprintLibrary.Unhandled()
+      }      
+    }
+
+    reset_button.OnClicked = () => this.restart();
+  };
+
+  KeyboardInputManager.prototype.restart = function (event) {
+    // event.preventDefault();
+    this.emit("restart");
+  };
+
+  KeyboardInputManager.prototype.keepPlaying = function (event) {
+    // event.preventDefault();
+    this.emit("keepPlaying");
+  };
+
+  KeyboardInputManager.prototype.bindButtonPress = function (selector, fn) {
+    // var button = document.querySelector(selector);
+    // button.addEventListener("click", fn.bind(this));
+    // button.addEventListener(this.eventTouchend, fn.bind(this));
+  };
+
+  return KeyboardInputManager
+}
diff --git a/Examples/Content/Scripts/2048/game/local_storage_manager.js b/Examples/Content/Scripts/2048/game/local_storage_manager.js
new file mode 100644
index 0000000..1f02e42
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/local_storage_manager.js
@@ -0,0 +1,65 @@
+window.fakeStorage = {
+  _data: {},
+
+  setItem: function (id, val) {
+    return this._data[id] = String(val);
+  },
+
+  getItem: function (id) {
+    return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
+  },
+
+  removeItem: function (id) {
+    return delete this._data[id];
+  },
+
+  clear: function () {
+    return this._data = {};
+  }
+};
+
+function LocalStorageManager() {
+  this.bestScoreKey     = "bestScore";
+  this.gameStateKey     = "gameState";
+
+  var supported = this.localStorageSupported();
+  this.storage = supported ? window.localStorage : window.fakeStorage;
+}
+
+LocalStorageManager.prototype.localStorageSupported = function () {
+  var testKey = "test";
+  var storage = window.localStorage;
+
+  try {
+    storage.setItem(testKey, "1");
+    storage.removeItem(testKey);
+    return true;
+  } catch (error) {
+    return false;
+  }
+};
+
+// Best score getters/setters
+LocalStorageManager.prototype.getBestScore = function () {
+  return this.storage.getItem(this.bestScoreKey) || 0;
+};
+
+LocalStorageManager.prototype.setBestScore = function (score) {
+  this.storage.setItem(this.bestScoreKey, score);
+};
+
+// Game state getters/setters and clearing
+LocalStorageManager.prototype.getGameState = function () {
+  var stateJSON = this.storage.getItem(this.gameStateKey);
+  return stateJSON ? JSON.parse(stateJSON) : null;
+};
+
+LocalStorageManager.prototype.setGameState = function (gameState) {
+  this.storage.setItem(this.gameStateKey, JSON.stringify(gameState));
+};
+
+LocalStorageManager.prototype.clearGameState = function () {
+  this.storage.removeItem(this.gameStateKey);
+};
+
+module.exports = LocalStorageManager
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/game/tile.js b/Examples/Content/Scripts/2048/game/tile.js
new file mode 100644
index 0000000..902a979
--- /dev/null
+++ b/Examples/Content/Scripts/2048/game/tile.js
@@ -0,0 +1,29 @@
+function Tile(position, value) {
+  this.x                = position.x;
+  this.y                = position.y;
+  this.value            = value || 2;
+
+  this.previousPosition = null;
+  this.mergedFrom       = null; // Tracks tiles that merged together
+}
+
+Tile.prototype.savePosition = function () {
+  this.previousPosition = { x: this.x, y: this.y };
+};
+
+Tile.prototype.updatePosition = function (position) {
+  this.x = position.x;
+  this.y = position.y;
+};
+
+Tile.prototype.serialize = function () {
+  return {
+    position: {
+      x: this.x,
+      y: this.y
+    },
+    value: this.value
+  };
+};
+
+module.exports = Tile
\ No newline at end of file
diff --git a/Examples/Content/Scripts/2048/index.js b/Examples/Content/Scripts/2048/index.js
new file mode 100644
index 0000000..6134c1b
--- /dev/null
+++ b/Examples/Content/Scripts/2048/index.js
@@ -0,0 +1,66 @@
+(function () {
+    "use strict";
+
+    module.exports = function () {        
+        function GetPC() {
+            return GWorld.GetAllActorsOfClass(PlayerController).OutActors[0]
+        }
+
+        let PC = GetPC()
+	
+        // Widget을 만들자
+        var widget = WidgetBlueprintLibrary.CreateWidget(GWorld, JavascriptWidget, PC)
+        widget.proxy = {}
+        widget.JavascriptContext = Context    
+        widget.bSupportsKeyboardFocus = true   
+		
+        // Setup location / rotation
+        PC.GetControlledPawn().SetActorLocation({X:854,Z:300})
+        PC.SetInitialLocationAndRotation({},{Yaw:170,Pitch:0,Roll:0})        
+
+        var data = {
+            Block : StaticMesh.Load('/Engine/BasicShapes/Cube.Cube'),
+            Spacing : 120,
+            Size: 4        
+        }	
+
+        var update = null        
+        var _ = require('lodash')    
+	
+        var page = new VerticalBox
+        page.Visibility = 'Visible'
+	
+        widget.SetRootWidget(page)
+        widget.AddToViewport()    
+
+        // UIOnly mode로 설정    
+        PC.bShowMouseCursor = true
+        WidgetBlueprintLibrary.SetInputMode_UIOnly(PC,page)
+
+        // Game 설정
+        var game = null
+
+        function start_game() {
+            if (game != null) {
+                game()
+            }
+            try {
+                game = require('game/application')(widget, page, data)
+            }
+            catch (e) {
+                console.error("EXCEPTION",String(e),e.stack)
+            }
+        }
+
+        start_game()
+
+        update = start_game
+	
+        return function () {
+            // for live reload
+            game()
+            widget.RemoveFromViewport()
+            // live_jade()        
+        }        
+    }    
+})()
\ No newline at end of file
diff --git a/Examples/Content/Scripts/aliases.js b/Examples/Content/Scripts/aliases.js
new file mode 100644
index 0000000..3453f55
--- /dev/null
+++ b/Examples/Content/Scripts/aliases.js
@@ -0,0 +1,745 @@
+UObject.prototype.ClearTimerbyFunctionName = UObject.prototype.K2_ClearTimer;
+UObject.prototype.GetTimerElapsedTimebyFunctionName = UObject.prototype.K2_GetTimerElapsedTime;
+UObject.prototype.GetTimerRemainingTimebyFunctionName = UObject.prototype.K2_GetTimerRemainingTime;
+UObject.prototype.IsTimerActivebyFunctionName = UObject.prototype.K2_IsTimerActive;
+UObject.prototype.IsTimerPausedbyFunctionName = UObject.prototype.K2_IsTimerPaused;
+UObject.prototype.PauseTimerbyFunctionName = UObject.prototype.K2_PauseTimer;
+UObject.prototype.SetTimerbyFunctionName = UObject.prototype.K2_SetTimer;
+UObject.prototype.DoesTimerExistbyFunctionName = UObject.prototype.K2_TimerExists;
+UObject.prototype.UnpauseTimerbyFunctionName = UObject.prototype.K2_UnPauseTimer;
+UObject.prototype.ToString = UObject.prototype.Conv_ObjectToString;
+UObject.prototype.Equal = UObject.prototype.EqualEqual_ObjectObject;
+UObject.prototype.NotEqual = UObject.prototype.NotEqual_ObjectObject;
+UObject.prototype.GetClass = UObject.prototype.GetObjectClass;
+Class.prototype.GetDisplayName = Class.prototype.GetClassDisplayName;
+Class.prototype.Equal = Class.prototype.EqualEqual_ClassClass;
+Class.prototype.NotEqual = Class.prototype.NotEqual_ClassClass;
+ActorComponent.prototype.Tick = ActorComponent.prototype.ReceiveTick;
+ActorComponent.prototype.EndPlay = ActorComponent.prototype.ReceiveEndPlay;
+ActorComponent.prototype.BeginPlay = ActorComponent.prototype.ReceiveBeginPlay;
+ActorComponent.prototype.DestroyComponent = ActorComponent.prototype.K2_DestroyComponent;
+ActorComponent.prototype.IsComponentBeingDestroyed = ActorComponent.prototype.IsBeingDestroyed;
+SceneComponent.prototype.SetWorldTransform = SceneComponent.prototype.K2_SetWorldTransform;
+SceneComponent.prototype.SetWorldRotation = SceneComponent.prototype.K2_SetWorldRotation;
+SceneComponent.prototype.SetWorldLocationAndRotation = SceneComponent.prototype.K2_SetWorldLocationAndRotation;
+SceneComponent.prototype.SetWorldLocation = SceneComponent.prototype.K2_SetWorldLocation;
+SceneComponent.prototype.SetRelativeTransform = SceneComponent.prototype.K2_SetRelativeTransform;
+SceneComponent.prototype.SetRelativeRotation = SceneComponent.prototype.K2_SetRelativeRotation;
+SceneComponent.prototype.SetRelativeLocationAndRotation = SceneComponent.prototype.K2_SetRelativeLocationAndRotation;
+SceneComponent.prototype.SetRelativeLocation = SceneComponent.prototype.K2_SetRelativeLocation;
+SceneComponent.prototype.GetWorldTransform = SceneComponent.prototype.K2_GetComponentToWorld;
+SceneComponent.prototype.GetWorldScale = SceneComponent.prototype.K2_GetComponentScale;
+SceneComponent.prototype.GetWorldRotation = SceneComponent.prototype.K2_GetComponentRotation;
+SceneComponent.prototype.GetWorldLocation = SceneComponent.prototype.K2_GetComponentLocation;
+SceneComponent.prototype.AttachTo = SceneComponent.prototype.K2_AttachTo;
+SceneComponent.prototype.AddWorldTransform = SceneComponent.prototype.K2_AddWorldTransform;
+SceneComponent.prototype.AddWorldRotation = SceneComponent.prototype.K2_AddWorldRotation;
+SceneComponent.prototype.AddWorldOffset = SceneComponent.prototype.K2_AddWorldOffset;
+SceneComponent.prototype.AddRelativeRotation = SceneComponent.prototype.K2_AddRelativeRotation;
+SceneComponent.prototype.AddRelativeLocation = SceneComponent.prototype.K2_AddRelativeLocation;
+SceneComponent.prototype.AddLocalTransform = SceneComponent.prototype.K2_AddLocalTransform;
+SceneComponent.prototype.AddLocalRotation = SceneComponent.prototype.K2_AddLocalRotation;
+SceneComponent.prototype.AddLocalOffset = SceneComponent.prototype.K2_AddLocalOffset;
+PrimitiveComponent.prototype.ComponentOverlapActors = PrimitiveComponent.prototype.ComponentOverlapActors_NEW;
+PrimitiveComponent.prototype.ComponentOverlapComponents = PrimitiveComponent.prototype.ComponentOverlapComponents_NEW;
+PrimitiveComponent.prototype.SetPhysicalMaterialOverride = PrimitiveComponent.prototype.SetPhysMaterialOverride;
+PrimitiveComponent.prototype.SetMaxDrawDistance = PrimitiveComponent.prototype.SetCullDistance;
+PrimitiveComponent.prototype.LineTraceComponent = PrimitiveComponent.prototype.K2_LineTraceComponent;
+PrimitiveComponent.prototype.CreateMIDForElementFromMaterial = PrimitiveComponent.prototype.CreateAndSetMaterialInstanceDynamicFromMaterial;
+PrimitiveComponent.prototype.CreateMIDForElement = PrimitiveComponent.prototype.CreateAndSetMaterialInstanceDynamic;
+PrimitiveComponent.prototype.GetMoveIgnoreActors = PrimitiveComponent.prototype.CopyArrayOfMoveIgnoreActors;
+Actor.prototype.ConstructionScript = Actor.prototype.UserConstructionScript;
+Actor.prototype.SnapActorTo = Actor.prototype.SnapRootComponentTo;
+Actor.prototype.Tick = Actor.prototype.ReceiveTick;
+Actor.prototype.RadialDamage = Actor.prototype.ReceiveRadialDamage;
+Actor.prototype.PointDamage = Actor.prototype.ReceivePointDamage;
+Actor.prototype.Hit = Actor.prototype.ReceiveHit;
+Actor.prototype.EndPlay = Actor.prototype.ReceiveEndPlay;
+Actor.prototype.Destroyed = Actor.prototype.ReceiveDestroyed;
+Actor.prototype.BeginPlay = Actor.prototype.ReceiveBeginPlay;
+Actor.prototype.AnyDamage = Actor.prototype.ReceiveAnyDamage;
+Actor.prototype.ActorOnReleased = Actor.prototype.ReceiveActorOnReleased;
+Actor.prototype.TouchLeave = Actor.prototype.ReceiveActorOnInputTouchLeave;
+Actor.prototype.TouchEnter = Actor.prototype.ReceiveActorOnInputTouchEnter;
+Actor.prototype.EndInputTouch = Actor.prototype.ReceiveActorOnInputTouchEnd;
+Actor.prototype.BeginInputTouch = Actor.prototype.ReceiveActorOnInputTouchBegin;
+Actor.prototype.ActorOnClicked = Actor.prototype.ReceiveActorOnClicked;
+Actor.prototype.ActorEndOverlap = Actor.prototype.ReceiveActorEndOverlap;
+Actor.prototype.ActorEndCursorOver = Actor.prototype.ReceiveActorEndCursorOver;
+Actor.prototype.ActorBeginOverlap = Actor.prototype.ReceiveActorBeginOverlap;
+Actor.prototype.ActorBeginCursorOver = Actor.prototype.ReceiveActorBeginCursorOver;
+Actor.prototype.Teleport = Actor.prototype.K2_TeleportTo;
+Actor.prototype.SetActorTransform = Actor.prototype.K2_SetActorTransform;
+Actor.prototype.SetActorRelativeTransform = Actor.prototype.K2_SetActorRelativeTransform;
+Actor.prototype.SetActorRelativeRotation = Actor.prototype.K2_SetActorRelativeRotation;
+Actor.prototype.SetActorRelativeLocation = Actor.prototype.K2_SetActorRelativeLocation;
+Actor.prototype.SetActorLocationAndRotation = Actor.prototype.K2_SetActorLocationAndRotation;
+Actor.prototype.SetActorLocation = Actor.prototype.K2_SetActorLocation;
+Actor.prototype.OnReset = Actor.prototype.K2_OnReset;
+Actor.prototype.OnEndViewTarget = Actor.prototype.K2_OnEndViewTarget;
+Actor.prototype.OnBecomeViewTarget = Actor.prototype.K2_OnBecomeViewTarget;
+Actor.prototype.GetRootComponent = Actor.prototype.K2_GetRootComponent;
+Actor.prototype.GetActorRotation = Actor.prototype.K2_GetActorRotation;
+Actor.prototype.GetActorLocation = Actor.prototype.K2_GetActorLocation;
+Actor.prototype.DestroyComponent = Actor.prototype.K2_DestroyComponent;
+Actor.prototype.DestroyActor = Actor.prototype.K2_DestroyActor;
+Actor.prototype.AttachActorToActor = Actor.prototype.K2_AttachRootComponentToActor;
+Actor.prototype.AttachActorToComponent = Actor.prototype.K2_AttachRootComponentTo;
+Actor.prototype.AddActorWorldTransform = Actor.prototype.K2_AddActorWorldTransform;
+Actor.prototype.AddActorWorldRotation = Actor.prototype.K2_AddActorWorldRotation;
+Actor.prototype.AddActorWorldOffset = Actor.prototype.K2_AddActorWorldOffset;
+Actor.prototype.AddActorLocalTransform = Actor.prototype.K2_AddActorLocalTransform;
+Actor.prototype.AddActorLocalRotation = Actor.prototype.K2_AddActorLocalRotation;
+Actor.prototype.AddActorLocalOffset = Actor.prototype.K2_AddActorLocalOffset;
+Actor.prototype.GetActorTransform = Actor.prototype.GetTransform;
+Actor.prototype.DetachActorFromActor = Actor.prototype.DetachRootComponentFromParent;
+PawnActionsComponent.prototype.PushAction = PawnActionsComponent.prototype.K2_PushAction;
+PawnActionsComponent.prototype.PerformAction = PawnActionsComponent.prototype.K2_PerformAction;
+PawnActionsComponent.PerformAction = PawnActionsComponent.K2_PerformAction;
+PawnActionsComponent.prototype.ForceAbortAction = PawnActionsComponent.prototype.K2_ForceAbortAction;
+PawnActionsComponent.prototype.AbortAction = PawnActionsComponent.prototype.K2_AbortAction;
+Controller.prototype.GetControlledPawn = Controller.prototype.K2_GetPawn;
+AIController.prototype.SetFocus = AIController.prototype.K2_SetFocus;
+AIController.prototype.SetFocalPoint = AIController.prototype.K2_SetFocalPoint;
+AIController.prototype.ClearFocus = AIController.prototype.K2_ClearFocus;
+AISense_Blueprint.prototype.OnNewPawn = AISense_Blueprint.prototype.K2_OnNewPawn;
+AITask_MoveTo.prototype.MoveToLocationorActor = AITask_MoveTo.prototype.AIMoveTo;
+AITask_MoveTo.MoveToLocationorActor = AITask_MoveTo.AIMoveTo;
+Pawn.prototype.Unpossessed = Pawn.prototype.ReceiveUnpossessed;
+Pawn.prototype.Possessed = Pawn.prototype.ReceivePossessed;
+Pawn.prototype.GetMovementInputVector = Pawn.prototype.K2_GetMovementInputVector;
+Character.prototype.UpdateCustomMovement = Character.prototype.K2_UpdateCustomMovement;
+Character.prototype.OnStartCrouch = Character.prototype.K2_OnStartCrouch;
+Character.prototype.OnMovementModeChanged = Character.prototype.K2_OnMovementModeChanged;
+Character.prototype.OnEndCrouch = Character.prototype.K2_OnEndCrouch;
+Character.prototype.CanJump = Character.prototype.CanJumpInternal;
+AnimInstance.prototype.GetTransitionTimeElapsed = AnimInstance.prototype.GetInstanceTransitionTimeElapsedFraction;
+AnimInstance.prototype.GetTransitionTimeElapsed = AnimInstance.prototype.GetInstanceTransitionTimeElapsed;
+AnimInstance.prototype.GetTransitionCrossfadeDuration = AnimInstance.prototype.GetInstanceTransitionCrossfadeDuration;
+AnimInstance.prototype.StateWeight = AnimInstance.prototype.GetInstanceStateWeight;
+AnimInstance.prototype.CurrentStateTime = AnimInstance.prototype.GetInstanceCurrentStateElapsedTime;
+AnimInstance.prototype.TimeRemaining = AnimInstance.prototype.GetInstanceAssetPlayerTimeFromEndFraction;
+AnimInstance.prototype.TimeRemaining = AnimInstance.prototype.GetInstanceAssetPlayerTimeFromEnd;
+AnimInstance.prototype.CurrentTime = AnimInstance.prototype.GetInstanceAssetPlayerTimeFraction;
+AnimInstance.prototype.CurrentTime = AnimInstance.prototype.GetInstanceAssetPlayerTime;
+AnimInstance.prototype.Length = AnimInstance.prototype.GetInstanceAssetPlayerLength;
+Widget.prototype.HasAnyUserFocusedDescendants = Widget.prototype.HasFocusedDescendants;
+WidgetBlueprintLibrary.prototype.DrawText = WidgetBlueprintLibrary.prototype.DrawTextFormatted;
+WidgetBlueprintLibrary.DrawText = WidgetBlueprintLibrary.DrawTextFormatted;
+WidgetBlueprintLibrary.prototype.DrawString = WidgetBlueprintLibrary.prototype.DrawText;
+WidgetBlueprintLibrary.DrawString = WidgetBlueprintLibrary.DrawText;
+WidgetBlueprintLibrary.prototype.CreateWidget = WidgetBlueprintLibrary.prototype.Create;
+WidgetBlueprintLibrary.CreateWidget = WidgetBlueprintLibrary.Create;
+GameInstance.prototype.Shutdown = GameInstance.prototype.ReceiveShutdown;
+GameInstance.prototype.Init = GameInstance.prototype.ReceiveInit;
+GameInstance.prototype.TravelError = GameInstance.prototype.HandleTravelError;
+GameInstance.prototype.NetworkError = GameInstance.prototype.HandleNetworkError;
+World.prototype.LogBoxShape = World.prototype.LogBox;
+World.prototype.BoxOverlapActors = World.prototype.BoxOverlapActors_NEW;
+World.prototype.BoxOverlapComponents = World.prototype.BoxOverlapComponents_NEW;
+World.prototype.MultiBoxTraceByChannel = World.prototype.BoxTraceMulti;
+World.prototype.MultiBoxTraceForObjects = World.prototype.BoxTraceMultiForObjects;
+World.prototype.BoxTraceByChannel = World.prototype.BoxTraceSingle;
+World.prototype.BoxTraceForObjects = World.prototype.BoxTraceSingleForObjects;
+World.prototype.CapsuleOverlapActors = World.prototype.CapsuleOverlapActors_NEW;
+World.prototype.CapsuleOverlapComponents = World.prototype.CapsuleOverlapComponents_NEW;
+World.prototype.MultiCapsuleTraceByChannelDeprecated = World.prototype.CapsuleTraceMulti_DEPRECATED;
+World.prototype.MultiCapsuleTraceByChannel = World.prototype.CapsuleTraceMulti_NEW;
+World.prototype.MultiCapsuleTraceByObjectDeprecated = World.prototype.CapsuleTraceMultiByObject_DEPRECATED;
+World.prototype.MultiCapsuleTraceForObjects = World.prototype.CapsuleTraceMultiForObjects;
+World.prototype.SingleCapsuleTraceByChannelDeprecated = World.prototype.CapsuleTraceSingle_DEPRECATED;
+World.prototype.CapsuleTraceByChannel = World.prototype.CapsuleTraceSingle_NEW;
+World.prototype.SingleCapsuleTraceByObjectDeprecated = World.prototype.CapsuleTraceSingleByObject_DEPRECATED;
+World.prototype.CapsuleTraceForObjects = World.prototype.CapsuleTraceSingleForObjects;
+World.prototype.DrawDebugCone = World.prototype.DrawDebugConeInDegrees;
+World.prototype.ClearTimerbyHandle = World.prototype.K2_ClearTimerHandle;
+World.prototype.GetTimerElapsedTimebyHandle = World.prototype.K2_GetTimerElapsedTimeHandle;
+World.prototype.GetTimerRemainingTimebyHandle = World.prototype.K2_GetTimerRemainingTimeHandle;
+World.prototype.IsTimerActivebyHandle = World.prototype.K2_IsTimerActiveHandle;
+World.prototype.IsTimerPausedbyHandle = World.prototype.K2_IsTimerPausedHandle;
+World.prototype.PauseTimerbyHandle = World.prototype.K2_PauseTimerHandle;
+World.prototype.DoesTimerExistbyHandle = World.prototype.K2_TimerExistsHandle;
+World.prototype.UnpauseTimerbyHandle = World.prototype.K2_UnPauseTimerHandle;
+World.prototype.MultiLineTraceByChannelDeprecated = World.prototype.LineTraceMulti_DEPRECATED;
+World.prototype.MultiLineTraceByChannel = World.prototype.LineTraceMulti_NEW;
+World.prototype.MultiLineTraceByObjectDeprecated = World.prototype.LineTraceMultiByObject_DEPRECATED;
+World.prototype.MultiLineTraceForObjects = World.prototype.LineTraceMultiForObjects;
+World.prototype.SingleLineTraceByChannelDeprecated = World.prototype.LineTraceSingle_DEPRECATED;
+World.prototype.LineTraceByChannel = World.prototype.LineTraceSingle_NEW;
+World.prototype.SingleLineTraceByObjectDeprecated = World.prototype.LineTraceSingleByObject_DEPRECATED;
+World.prototype.LineTraceForObjects = World.prototype.LineTraceSingleForObjects;
+World.prototype.SphereOverlapActors = World.prototype.SphereOverlapActors_NEW;
+World.prototype.SphereOverlapComponents = World.prototype.SphereOverlapComponents_NEW;
+World.prototype.MultiSphereTraceByChannelDeprecated = World.prototype.SphereTraceMulti_DEPRECATED;
+World.prototype.MultiSphereTraceByChannel = World.prototype.SphereTraceMulti_NEW;
+World.prototype.MultiSphereTraceByObjectDeprecated = World.prototype.SphereTraceMultiByObject_DEPRECATED;
+World.prototype.MultiSphereTraceForObjects = World.prototype.SphereTraceMultiForObjects;
+World.prototype.SingleSphereTraceByChannelDeprecated = World.prototype.SphereTraceSingle_DEPRECATED;
+World.prototype.SphereTraceByChannel = World.prototype.SphereTraceSingle_NEW;
+World.prototype.SingleSphereTraceByObjectDeprecated = World.prototype.SphereTraceSingleByObject_DEPRECATED;
+World.prototype.SphereTraceForObjects = World.prototype.SphereTraceSingleForObjects;
+World.prototype.SuggestProjectileVelocity = World.prototype.BlueprintSuggestProjectileVelocity;
+World.prototype.CreateWidget = World.prototype.Create;
+PlayerController.prototype.ConvertWorldLocationToScreenLocation = PlayerController.prototype.ProjectWorldLocationToScreen;
+PlayerController.prototype.ConvertScreenLocationToWorldSpace = PlayerController.prototype.DeprojectScreenPositionToWorld;
+PlayerController.prototype.ConvertMouseLocationToWorldSpace = PlayerController.prototype.DeprojectMousePositionToWorld;
+HUD.prototype.HitBoxReleased = HUD.prototype.ReceiveHitBoxRelease;
+HUD.prototype.HitBoxEndCursorOver = HUD.prototype.ReceiveHitBoxEndCursorOver;
+HUD.prototype.HitBoxClicked = HUD.prototype.ReceiveHitBoxClick;
+HUD.prototype.HitBoxBeginCursorOver = HUD.prototype.ReceiveHitBoxBeginCursorOver;
+GameMode.prototype.OnPostLogin = GameMode.prototype.K2_PostLogin;
+GameMode.prototype.OnSwapPlayerControllers = GameMode.prototype.K2_OnSwapPlayerControllers;
+GameMode.prototype.OnSetMatchState = GameMode.prototype.K2_OnSetMatchState;
+GameMode.prototype.OnRestartPlayer = GameMode.prototype.K2_OnRestartPlayer;
+GameMode.prototype.OnLogout = GameMode.prototype.K2_OnLogout;
+GameMode.prototype.OnChangeName = GameMode.prototype.K2_OnChangeName;
+GameMode.prototype.FindPlayerStart = GameMode.prototype.K2_FindPlayerStart;
+MovementComponent.prototype.MoveUpdatedComponent = MovementComponent.prototype.K2_MoveUpdatedComponent;
+MovementComponent.prototype.GetModifiedMaxSpeed = MovementComponent.prototype.K2_GetModifiedMaxSpeed;
+MovementComponent.prototype.GetMaxSpeedModifier = MovementComponent.prototype.K2_GetMaxSpeedModifier;
+PawnMovementComponent.prototype.GetInputVector = PawnMovementComponent.prototype.K2_GetInputVector;
+CharacterMovementComponent.prototype.GetWalkableFloorZ = CharacterMovementComponent.prototype.K2_GetWalkableFloorZ;
+CharacterMovementComponent.prototype.GetWalkableFloorAngle = CharacterMovementComponent.prototype.K2_GetWalkableFloorAngle;
+CharacterMovementComponent.prototype.GetModifiedMaxAcceleration = CharacterMovementComponent.prototype.K2_GetModifiedMaxAcceleration;
+AudioComponent.prototype.SetIntegerParameter = AudioComponent.prototype.SetIntParameter;
+AudioComponent.prototype.SetBooleanParameter = AudioComponent.prototype.SetBoolParameter;
+AudioComponent.prototype.GetAttenuationSettingsToApply = AudioComponent.prototype.BP_GetAttenuationSettingsToApply;
+ArrowComponent.prototype.SetArrowColor = ArrowComponent.prototype.SetArrowColor_New;
+TextRenderComponent.prototype.SetText = TextRenderComponent.prototype.K2_SetText;
+GameplayStatics.prototype.GetClass = GameplayStatics.prototype.GetObjectClass;
+GameplayStatics.GetClass = GameplayStatics.GetObjectClass;
+GameplayStatics.prototype.SuggestProjectileVelocity = GameplayStatics.prototype.BlueprintSuggestProjectileVelocity;
+GameplayStatics.SuggestProjectileVelocity = GameplayStatics.BlueprintSuggestProjectileVelocity;
+KismetArrayLibrary.prototype.Shuffle = KismetArrayLibrary.prototype.Array_Shuffle;
+KismetArrayLibrary.Shuffle = KismetArrayLibrary.Array_Shuffle;
+KismetArrayLibrary.prototype.SetArrayElem = KismetArrayLibrary.prototype.Array_Set;
+KismetArrayLibrary.SetArrayElem = KismetArrayLibrary.Array_Set;
+KismetArrayLibrary.prototype.Resize = KismetArrayLibrary.prototype.Array_Resize;
+KismetArrayLibrary.Resize = KismetArrayLibrary.Array_Resize;
+KismetArrayLibrary.prototype.RemoveItem = KismetArrayLibrary.prototype.Array_RemoveItem;
+KismetArrayLibrary.RemoveItem = KismetArrayLibrary.Array_RemoveItem;
+KismetArrayLibrary.prototype.RemoveIndex = KismetArrayLibrary.prototype.Array_Remove;
+KismetArrayLibrary.RemoveIndex = KismetArrayLibrary.Array_Remove;
+KismetArrayLibrary.prototype.Length = KismetArrayLibrary.prototype.Array_Length;
+KismetArrayLibrary.Length = KismetArrayLibrary.Array_Length;
+KismetArrayLibrary.prototype.LastIndex = KismetArrayLibrary.prototype.Array_LastIndex;
+KismetArrayLibrary.LastIndex = KismetArrayLibrary.Array_LastIndex;
+KismetArrayLibrary.prototype.Insert = KismetArrayLibrary.prototype.Array_Insert;
+KismetArrayLibrary.Insert = KismetArrayLibrary.Array_Insert;
+KismetArrayLibrary.prototype.Get = KismetArrayLibrary.prototype.Array_Get;
+KismetArrayLibrary.Get = KismetArrayLibrary.Array_Get;
+KismetArrayLibrary.prototype.FindItem = KismetArrayLibrary.prototype.Array_Find;
+KismetArrayLibrary.FindItem = KismetArrayLibrary.Array_Find;
+KismetArrayLibrary.prototype.ContainsItem = KismetArrayLibrary.prototype.Array_Contains;
+KismetArrayLibrary.ContainsItem = KismetArrayLibrary.Array_Contains;
+KismetArrayLibrary.prototype.Clear = KismetArrayLibrary.prototype.Array_Clear;
+KismetArrayLibrary.Clear = KismetArrayLibrary.Array_Clear;
+KismetArrayLibrary.prototype.AppendArray = KismetArrayLibrary.prototype.Array_Append;
+KismetArrayLibrary.AppendArray = KismetArrayLibrary.Array_Append;
+KismetArrayLibrary.prototype.AddUnique = KismetArrayLibrary.prototype.Array_AddUnique;
+KismetArrayLibrary.AddUnique = KismetArrayLibrary.Array_AddUnique;
+KismetArrayLibrary.prototype.Add = KismetArrayLibrary.prototype.Array_Add;
+KismetArrayLibrary.Add = KismetArrayLibrary.Array_Add;
+KismetGuidLibrary.prototype.ParseStringtoGuid = KismetGuidLibrary.prototype.Parse_StringToGuid;
+KismetGuidLibrary.ParseStringtoGuid = KismetGuidLibrary.Parse_StringToGuid;
+KismetGuidLibrary.prototype.NotEqual = KismetGuidLibrary.prototype.NotEqual_GuidGuid;
+KismetGuidLibrary.NotEqual = KismetGuidLibrary.NotEqual_GuidGuid;
+KismetGuidLibrary.prototype.IsValid = KismetGuidLibrary.prototype.IsValid_Guid;
+KismetGuidLibrary.IsValid = KismetGuidLibrary.IsValid_Guid;
+KismetGuidLibrary.prototype.Equal = KismetGuidLibrary.prototype.EqualEqual_GuidGuid;
+KismetGuidLibrary.Equal = KismetGuidLibrary.EqualEqual_GuidGuid;
+KismetGuidLibrary.prototype.ToString = KismetGuidLibrary.prototype.Conv_GuidToString;
+KismetGuidLibrary.ToString = KismetGuidLibrary.Conv_GuidToString;
+KismetInputLibrary.prototype.IsTouchEvent = KismetInputLibrary.prototype.PointerEvent_IsTouchEvent;
+KismetInputLibrary.IsTouchEvent = KismetInputLibrary.PointerEvent_IsTouchEvent;
+KismetInputLibrary.prototype.IsMouseButtonDown = KismetInputLibrary.prototype.PointerEvent_IsMouseButtonDown;
+KismetInputLibrary.IsMouseButtonDown = KismetInputLibrary.PointerEvent_IsMouseButtonDown;
+KismetInputLibrary.prototype.GetWheelDelta = KismetInputLibrary.prototype.PointerEvent_GetWheelDelta;
+KismetInputLibrary.GetWheelDelta = KismetInputLibrary.PointerEvent_GetWheelDelta;
+KismetInputLibrary.prototype.GetUserIndex = KismetInputLibrary.prototype.PointerEvent_GetUserIndex;
+KismetInputLibrary.GetUserIndex = KismetInputLibrary.PointerEvent_GetUserIndex;
+KismetInputLibrary.prototype.GetTouchpadIndex = KismetInputLibrary.prototype.PointerEvent_GetTouchpadIndex;
+KismetInputLibrary.GetTouchpadIndex = KismetInputLibrary.PointerEvent_GetTouchpadIndex;
+KismetInputLibrary.prototype.GetScreenSpacePosition = KismetInputLibrary.prototype.PointerEvent_GetScreenSpacePosition;
+KismetInputLibrary.GetScreenSpacePosition = KismetInputLibrary.PointerEvent_GetScreenSpacePosition;
+KismetInputLibrary.prototype.GetPointerIndex = KismetInputLibrary.prototype.PointerEvent_GetPointerIndex;
+KismetInputLibrary.GetPointerIndex = KismetInputLibrary.PointerEvent_GetPointerIndex;
+KismetInputLibrary.prototype.GetLastScreenSpacePosition = KismetInputLibrary.prototype.PointerEvent_GetLastScreenSpacePosition;
+KismetInputLibrary.GetLastScreenSpacePosition = KismetInputLibrary.PointerEvent_GetLastScreenSpacePosition;
+KismetInputLibrary.prototype.GetGestureDelta = KismetInputLibrary.prototype.PointerEvent_GetGestureDelta;
+KismetInputLibrary.GetGestureDelta = KismetInputLibrary.PointerEvent_GetGestureDelta;
+KismetInputLibrary.prototype.GetEffectingButton = KismetInputLibrary.prototype.PointerEvent_GetEffectingButton;
+KismetInputLibrary.GetEffectingButton = KismetInputLibrary.PointerEvent_GetEffectingButton;
+KismetInputLibrary.prototype.GetCursorDelta = KismetInputLibrary.prototype.PointerEvent_GetCursorDelta;
+KismetInputLibrary.GetCursorDelta = KismetInputLibrary.PointerEvent_GetCursorDelta;
+KismetInputLibrary.prototype.IsShiftDown = KismetInputLibrary.prototype.InputEvent_IsShiftDown;
+KismetInputLibrary.IsShiftDown = KismetInputLibrary.InputEvent_IsShiftDown;
+KismetInputLibrary.prototype.IsRightShiftDown = KismetInputLibrary.prototype.InputEvent_IsRightShiftDown;
+KismetInputLibrary.IsRightShiftDown = KismetInputLibrary.InputEvent_IsRightShiftDown;
+KismetInputLibrary.prototype.IsRightControlDown = KismetInputLibrary.prototype.InputEvent_IsRightControlDown;
+KismetInputLibrary.IsRightControlDown = KismetInputLibrary.InputEvent_IsRightControlDown;
+KismetInputLibrary.prototype.IsRightCommandDown = KismetInputLibrary.prototype.InputEvent_IsRightCommandDown;
+KismetInputLibrary.IsRightCommandDown = KismetInputLibrary.InputEvent_IsRightCommandDown;
+KismetInputLibrary.prototype.IsRightAltDown = KismetInputLibrary.prototype.InputEvent_IsRightAltDown;
+KismetInputLibrary.IsRightAltDown = KismetInputLibrary.InputEvent_IsRightAltDown;
+KismetInputLibrary.prototype.IsRepeat = KismetInputLibrary.prototype.InputEvent_IsRepeat;
+KismetInputLibrary.IsRepeat = KismetInputLibrary.InputEvent_IsRepeat;
+KismetInputLibrary.prototype.IsLeftShiftDown = KismetInputLibrary.prototype.InputEvent_IsLeftShiftDown;
+KismetInputLibrary.IsLeftShiftDown = KismetInputLibrary.InputEvent_IsLeftShiftDown;
+KismetInputLibrary.prototype.IsLeftControlDown = KismetInputLibrary.prototype.InputEvent_IsLeftControlDown;
+KismetInputLibrary.IsLeftControlDown = KismetInputLibrary.InputEvent_IsLeftControlDown;
+KismetInputLibrary.prototype.IsLeftCommandDown = KismetInputLibrary.prototype.InputEvent_IsLeftCommandDown;
+KismetInputLibrary.IsLeftCommandDown = KismetInputLibrary.InputEvent_IsLeftCommandDown;
+KismetInputLibrary.prototype.IsLeftAltDown = KismetInputLibrary.prototype.InputEvent_IsLeftAltDown;
+KismetInputLibrary.IsLeftAltDown = KismetInputLibrary.InputEvent_IsLeftAltDown;
+KismetInputLibrary.prototype.IsControlDown = KismetInputLibrary.prototype.InputEvent_IsControlDown;
+KismetInputLibrary.IsControlDown = KismetInputLibrary.InputEvent_IsControlDown;
+KismetInputLibrary.prototype.IsCommandDown = KismetInputLibrary.prototype.InputEvent_IsCommandDown;
+KismetInputLibrary.IsCommandDown = KismetInputLibrary.InputEvent_IsCommandDown;
+KismetInputLibrary.prototype.IsAltDown = KismetInputLibrary.prototype.InputEvent_IsAltDown;
+KismetInputLibrary.IsAltDown = KismetInputLibrary.InputEvent_IsAltDown;
+KismetInputLibrary.prototype.Equal = KismetInputLibrary.prototype.EqualEqual_KeyKey;
+KismetInputLibrary.Equal = KismetInputLibrary.EqualEqual_KeyKey;
+KismetInputLibrary.prototype.GetEffectingButton = KismetInputLibrary.prototype.ControllerEvent_GetEffectingButton;
+KismetInputLibrary.GetEffectingButton = KismetInputLibrary.ControllerEvent_GetEffectingButton;
+KismetMathLibrary.prototype.BitwiseXOR = KismetMathLibrary.prototype.Xor_IntInt;
+KismetMathLibrary.BitwiseXOR = KismetMathLibrary.Xor_IntInt;
+KismetMathLibrary.prototype.VectorLengthSquared = KismetMathLibrary.prototype.VSizeSquared;
+KismetMathLibrary.VectorLengthSquared = KismetMathLibrary.VSizeSquared;
+KismetMathLibrary.prototype.Vector2dLengthSquared = KismetMathLibrary.prototype.VSize2DSquared;
+KismetMathLibrary.Vector2dLengthSquared = KismetMathLibrary.VSize2DSquared;
+KismetMathLibrary.prototype.Vector2dLength = KismetMathLibrary.prototype.VSize2D;
+KismetMathLibrary.Vector2dLength = KismetMathLibrary.VSize2D;
+KismetMathLibrary.prototype.VectorLength = KismetMathLibrary.prototype.VSize;
+KismetMathLibrary.VectorLength = KismetMathLibrary.VSize;
+KismetMathLibrary.prototype.Lerp = KismetMathLibrary.prototype.VLerp;
+KismetMathLibrary.Lerp = KismetMathLibrary.VLerp;
+KismetMathLibrary.prototype.Ease = KismetMathLibrary.prototype.VEase;
+KismetMathLibrary.Ease = KismetMathLibrary.VEase;
+KismetMathLibrary.prototype.Lerp = KismetMathLibrary.prototype.TLerp;
+KismetMathLibrary.Lerp = KismetMathLibrary.TLerp;
+KismetMathLibrary.prototype.ZeroValue = KismetMathLibrary.prototype.TimespanZeroValue;
+KismetMathLibrary.ZeroValue = KismetMathLibrary.TimespanZeroValue;
+KismetMathLibrary.prototype.MinValue = KismetMathLibrary.prototype.TimespanMinValue;
+KismetMathLibrary.MinValue = KismetMathLibrary.TimespanMinValue;
+KismetMathLibrary.prototype.MaxValue = KismetMathLibrary.prototype.TimespanMaxValue;
+KismetMathLibrary.MaxValue = KismetMathLibrary.TimespanMaxValue;
+KismetMathLibrary.prototype.Ease = KismetMathLibrary.prototype.TEase;
+KismetMathLibrary.Ease = KismetMathLibrary.TEase;
+KismetMathLibrary.prototype.Sign = KismetMathLibrary.prototype.SignOfInteger;
+KismetMathLibrary.Sign = KismetMathLibrary.SignOfInteger;
+KismetMathLibrary.prototype.Sign = KismetMathLibrary.prototype.SignOfFloat;
+KismetMathLibrary.Sign = KismetMathLibrary.SignOfFloat;
+KismetMathLibrary.prototype.RotateVectorAroundAxis = KismetMathLibrary.prototype.RotateAngleAxis;
+KismetMathLibrary.RotateVectorAroundAxis = KismetMathLibrary.RotateAngleAxis;
+KismetMathLibrary.prototype.Lerp = KismetMathLibrary.prototype.RLerp;
+KismetMathLibrary.Lerp = KismetMathLibrary.RLerp;
+KismetMathLibrary.prototype.RGBtoHSV = KismetMathLibrary.prototype.RGBToHSV_Vector;
+KismetMathLibrary.RGBtoHSV = KismetMathLibrary.RGBToHSV_Vector;
+KismetMathLibrary.prototype.Ease = KismetMathLibrary.prototype.REase;
+KismetMathLibrary.Ease = KismetMathLibrary.REase;
+KismetMathLibrary.prototype.BitwiseOR = KismetMathLibrary.prototype.Or_IntInt;
+KismetMathLibrary.BitwiseOR = KismetMathLibrary.Or_IntInt;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_VectorVector;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_VectorVector;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_TimespanTimespan;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_TimespanTimespan;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_RotatorRotator;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_RotatorRotator;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_ObjectObject;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_ObjectObject;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_NameName;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_NameName;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_IntInt;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_IntInt;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_FloatFloat;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_FloatFloat;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_DateTimeDateTime;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_DateTimeDateTime;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_ClassClass;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_ClassClass;
+KismetMathLibrary.prototype.NotEqual = KismetMathLibrary.prototype.NotEqual_ByteByte;
+KismetMathLibrary.NotEqual = KismetMathLibrary.NotEqual_ByteByte;
+KismetMathLibrary.prototype.NotEqualBoolean = KismetMathLibrary.prototype.NotEqual_BoolBool;
+KismetMathLibrary.NotEqualBoolean = KismetMathLibrary.NotEqual_BoolBool;
+KismetMathLibrary.prototype.NOTBoolean = KismetMathLibrary.prototype.Not_PreBool;
+KismetMathLibrary.NOTBoolean = KismetMathLibrary.Not_PreBool;
+KismetMathLibrary.prototype.Delta = KismetMathLibrary.prototype.NormalizedDeltaRotator;
+KismetMathLibrary.Delta = KismetMathLibrary.NormalizedDeltaRotator;
+KismetMathLibrary.prototype.Normalize2D = KismetMathLibrary.prototype.Normal2D;
+KismetMathLibrary.Normalize2D = KismetMathLibrary.Normal2D;
+KismetMathLibrary.prototype.Normalize = KismetMathLibrary.prototype.Normal;
+KismetMathLibrary.Normalize = KismetMathLibrary.Normal;
+KismetMathLibrary.prototype.InvertRotator = KismetMathLibrary.prototype.NegateRotator;
+KismetMathLibrary.InvertRotator = KismetMathLibrary.NegateRotator;
+KismetMathLibrary.prototype.NearlyEqual = KismetMathLibrary.prototype.NearlyEqual_TransformTransform;
+KismetMathLibrary.NearlyEqual = KismetMathLibrary.NearlyEqual_TransformTransform;
+KismetMathLibrary.prototype.NearlyEqual = KismetMathLibrary.prototype.NearlyEqual_FloatFloat;
+KismetMathLibrary.NearlyEqual = KismetMathLibrary.NearlyEqual_FloatFloat;
+KismetMathLibrary.prototype.Power = KismetMathLibrary.prototype.MultiplyMultiply_FloatFloat;
+KismetMathLibrary.Power = KismetMathLibrary.MultiplyMultiply_FloatFloat;
+KismetMathLibrary.prototype.ScaleRotator = KismetMathLibrary.prototype.Multiply_RotatorInt;
+KismetMathLibrary.ScaleRotator = KismetMathLibrary.Multiply_RotatorInt;
+KismetMathLibrary.prototype.ScaleRotator = KismetMathLibrary.prototype.Multiply_RotatorFloat;
+KismetMathLibrary.ScaleRotator = KismetMathLibrary.Multiply_RotatorFloat;
+KismetMathLibrary.prototype.LinePlaneIntersection = KismetMathLibrary.prototype.LinePlaneIntersection_OriginNormal;
+KismetMathLibrary.LinePlaneIntersection = KismetMathLibrary.LinePlaneIntersection_OriginNormal;
+KismetMathLibrary.prototype.LerpUsingHSV = KismetMathLibrary.prototype.LinearColorLerpUsingHSV;
+KismetMathLibrary.LerpUsingHSV = KismetMathLibrary.LinearColorLerpUsingHSV;
+KismetMathLibrary.prototype.Lerp = KismetMathLibrary.prototype.LinearColorLerp;
+KismetMathLibrary.Lerp = KismetMathLibrary.LinearColorLerp;
+KismetMathLibrary.prototype.UnrotateVector = KismetMathLibrary.prototype.LessLess_VectorRotator;
+KismetMathLibrary.UnrotateVector = KismetMathLibrary.LessLess_VectorRotator;
+KismetMathLibrary.prototype.InRange = KismetMathLibrary.prototype.InRange_FloatFloat;
+KismetMathLibrary.InRange = KismetMathLibrary.InRange_FloatFloat;
+KismetMathLibrary.prototype.HSVtoRGB = KismetMathLibrary.prototype.HSVToRGB_Vector;
+KismetMathLibrary.HSVtoRGB = KismetMathLibrary.HSVToRGB_Vector;
+KismetMathLibrary.prototype.Snaptogrid = KismetMathLibrary.prototype.GridSnap_Float;
+KismetMathLibrary.Snaptogrid = KismetMathLibrary.GridSnap_Float;
+KismetMathLibrary.prototype.RotateVector = KismetMathLibrary.prototype.GreaterGreater_VectorRotator;
+KismetMathLibrary.RotateVector = KismetMathLibrary.GreaterGreater_VectorRotator;
+KismetMathLibrary.prototype.Truncate = KismetMathLibrary.prototype.FTrunc;
+KismetMathLibrary.Truncate = KismetMathLibrary.FTrunc;
+KismetMathLibrary.prototype.Division = KismetMathLibrary.prototype.FMod;
+KismetMathLibrary.Division = KismetMathLibrary.FMod;
+KismetMathLibrary.prototype.Min = KismetMathLibrary.prototype.FMin;
+KismetMathLibrary.Min = KismetMathLibrary.FMin;
+KismetMathLibrary.prototype.Max = KismetMathLibrary.prototype.FMax;
+KismetMathLibrary.Max = KismetMathLibrary.FMax;
+KismetMathLibrary.prototype.Floor = KismetMathLibrary.prototype.FFloor;
+KismetMathLibrary.Floor = KismetMathLibrary.FFloor;
+KismetMathLibrary.prototype.Clamp = KismetMathLibrary.prototype.FClamp;
+KismetMathLibrary.Clamp = KismetMathLibrary.FClamp;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_VectorVector;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_VectorVector;
+KismetMathLibrary.prototype.EqualTransform = KismetMathLibrary.prototype.EqualEqual_TransformTransform;
+KismetMathLibrary.EqualTransform = KismetMathLibrary.EqualEqual_TransformTransform;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_TimespanTimespan;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_TimespanTimespan;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_RotatorRotator;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_RotatorRotator;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_ObjectObject;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_ObjectObject;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_NameName;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_NameName;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_IntInt;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_IntInt;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_FloatFloat;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_FloatFloat;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_DateTimeDateTime;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_DateTimeDateTime;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_ClassClass;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_ClassClass;
+KismetMathLibrary.prototype.Equal = KismetMathLibrary.prototype.EqualEqual_ByteByte;
+KismetMathLibrary.Equal = KismetMathLibrary.EqualEqual_ByteByte;
+KismetMathLibrary.prototype.EqualBoolean = KismetMathLibrary.prototype.EqualEqual_BoolBool;
+KismetMathLibrary.EqualBoolean = KismetMathLibrary.EqualEqual_BoolBool;
+KismetMathLibrary.prototype.DotProduct = KismetMathLibrary.prototype.DotProduct2D;
+KismetMathLibrary.DotProduct = KismetMathLibrary.DotProduct2D;
+KismetMathLibrary.prototype.DotProduct = KismetMathLibrary.prototype.Dot_VectorVector;
+KismetMathLibrary.DotProduct = KismetMathLibrary.Dot_VectorVector;
+KismetMathLibrary.prototype.Tan = KismetMathLibrary.prototype.DegTan;
+KismetMathLibrary.Tan = KismetMathLibrary.DegTan;
+KismetMathLibrary.prototype.Sin = KismetMathLibrary.prototype.DegSin;
+KismetMathLibrary.Sin = KismetMathLibrary.DegSin;
+KismetMathLibrary.prototype.Cos = KismetMathLibrary.prototype.DegCos;
+KismetMathLibrary.Cos = KismetMathLibrary.DegCos;
+KismetMathLibrary.prototype.Atan2 = KismetMathLibrary.prototype.DegAtan2;
+KismetMathLibrary.Atan2 = KismetMathLibrary.DegAtan2;
+KismetMathLibrary.prototype.Atan = KismetMathLibrary.prototype.DegAtan;
+KismetMathLibrary.Atan = KismetMathLibrary.DegAtan;
+KismetMathLibrary.prototype.Asin = KismetMathLibrary.prototype.DegAsin;
+KismetMathLibrary.Asin = KismetMathLibrary.DegAsin;
+KismetMathLibrary.prototype.Acos = KismetMathLibrary.prototype.DegAcos;
+KismetMathLibrary.Acos = KismetMathLibrary.DegAcos;
+KismetMathLibrary.prototype.MinValue = KismetMathLibrary.prototype.DateTimeMinValue;
+KismetMathLibrary.MinValue = KismetMathLibrary.DateTimeMinValue;
+KismetMathLibrary.prototype.MaxValue = KismetMathLibrary.prototype.DateTimeMaxValue;
+KismetMathLibrary.MaxValue = KismetMathLibrary.DateTimeMaxValue;
+KismetMathLibrary.prototype.CrossProduct = KismetMathLibrary.prototype.CrossProduct2D;
+KismetMathLibrary.CrossProduct = KismetMathLibrary.CrossProduct2D;
+KismetMathLibrary.prototype.CrossProduct = KismetMathLibrary.prototype.Cross_VectorVector;
+KismetMathLibrary.CrossProduct = KismetMathLibrary.Cross_VectorVector;
+KismetMathLibrary.prototype.ToVector2D = KismetMathLibrary.prototype.Conv_VectorToVector2D;
+KismetMathLibrary.ToVector2D = KismetMathLibrary.Conv_VectorToVector2D;
+KismetMathLibrary.prototype.ToTransform = KismetMathLibrary.prototype.Conv_VectorToTransform;
+KismetMathLibrary.ToTransform = KismetMathLibrary.Conv_VectorToTransform;
+KismetMathLibrary.prototype.RotationFromXVector = KismetMathLibrary.prototype.Conv_VectorToRotator;
+KismetMathLibrary.RotationFromXVector = KismetMathLibrary.Conv_VectorToRotator;
+KismetMathLibrary.prototype.ToLinearColor = KismetMathLibrary.prototype.Conv_VectorToLinearColor;
+KismetMathLibrary.ToLinearColor = KismetMathLibrary.Conv_VectorToLinearColor;
+KismetMathLibrary.prototype.ToVector = KismetMathLibrary.prototype.Conv_Vector2DToVector;
+KismetMathLibrary.ToVector = KismetMathLibrary.Conv_Vector2DToVector;
+KismetMathLibrary.prototype.GetRotationXVector = KismetMathLibrary.prototype.Conv_RotatorToVector;
+KismetMathLibrary.GetRotationXVector = KismetMathLibrary.Conv_RotatorToVector;
+KismetMathLibrary.prototype.ToVector = KismetMathLibrary.prototype.Conv_LinearColorToVector;
+KismetMathLibrary.ToVector = KismetMathLibrary.Conv_LinearColorToVector;
+KismetMathLibrary.prototype.ToColor = KismetMathLibrary.prototype.Conv_LinearColorToColor;
+KismetMathLibrary.ToColor = KismetMathLibrary.Conv_LinearColorToColor;
+KismetMathLibrary.prototype.ToFloat = KismetMathLibrary.prototype.Conv_IntToFloat;
+KismetMathLibrary.ToFloat = KismetMathLibrary.Conv_IntToFloat;
+KismetMathLibrary.prototype.ToByte = KismetMathLibrary.prototype.Conv_IntToByte;
+KismetMathLibrary.ToByte = KismetMathLibrary.Conv_IntToByte;
+KismetMathLibrary.prototype.ToBool = KismetMathLibrary.prototype.Conv_IntToBool;
+KismetMathLibrary.ToBool = KismetMathLibrary.Conv_IntToBool;
+KismetMathLibrary.prototype.ToVector = KismetMathLibrary.prototype.Conv_FloatToVector;
+KismetMathLibrary.ToVector = KismetMathLibrary.Conv_FloatToVector;
+KismetMathLibrary.prototype.ToLinearColor = KismetMathLibrary.prototype.Conv_FloatToLinearColor;
+KismetMathLibrary.ToLinearColor = KismetMathLibrary.Conv_FloatToLinearColor;
+KismetMathLibrary.prototype.ToLinearColor = KismetMathLibrary.prototype.Conv_ColorToLinearColor;
+KismetMathLibrary.ToLinearColor = KismetMathLibrary.Conv_ColorToLinearColor;
+KismetMathLibrary.prototype.ToInt = KismetMathLibrary.prototype.Conv_ByteToInt;
+KismetMathLibrary.ToInt = KismetMathLibrary.Conv_ByteToInt;
+KismetMathLibrary.prototype.ToFloat = KismetMathLibrary.prototype.Conv_ByteToFloat;
+KismetMathLibrary.ToFloat = KismetMathLibrary.Conv_ByteToFloat;
+KismetMathLibrary.prototype.ToInt = KismetMathLibrary.prototype.Conv_BoolToInt;
+KismetMathLibrary.ToInt = KismetMathLibrary.Conv_BoolToInt;
+KismetMathLibrary.prototype.ToFloat = KismetMathLibrary.prototype.Conv_BoolToFloat;
+KismetMathLibrary.ToFloat = KismetMathLibrary.Conv_BoolToFloat;
+KismetMathLibrary.prototype.ToByte = KismetMathLibrary.prototype.Conv_BoolToByte;
+KismetMathLibrary.ToByte = KismetMathLibrary.Conv_BoolToByte;
+KismetMathLibrary.prototype.CombineRotators = KismetMathLibrary.prototype.ComposeRotators;
+KismetMathLibrary.CombineRotators = KismetMathLibrary.ComposeRotators;
+KismetMathLibrary.prototype.XORBoolean = KismetMathLibrary.prototype.BooleanXOR;
+KismetMathLibrary.XORBoolean = KismetMathLibrary.BooleanXOR;
+KismetMathLibrary.prototype.ORBoolean = KismetMathLibrary.prototype.BooleanOR;
+KismetMathLibrary.ORBoolean = KismetMathLibrary.BooleanOR;
+KismetMathLibrary.prototype.NORBoolean = KismetMathLibrary.prototype.BooleanNOR;
+KismetMathLibrary.NORBoolean = KismetMathLibrary.BooleanNOR;
+KismetMathLibrary.prototype.NANDBoolean = KismetMathLibrary.prototype.BooleanNAND;
+KismetMathLibrary.NANDBoolean = KismetMathLibrary.BooleanNAND;
+KismetMathLibrary.prototype.ANDBoolean = KismetMathLibrary.prototype.BooleanAND;
+KismetMathLibrary.ANDBoolean = KismetMathLibrary.BooleanAND;
+KismetMathLibrary.prototype.BitwiseAND = KismetMathLibrary.prototype.And_IntInt;
+KismetMathLibrary.BitwiseAND = KismetMathLibrary.And_IntInt;
+KismetMathLibrary.prototype.Absolute = KismetMathLibrary.prototype.Abs_Int;
+KismetMathLibrary.Absolute = KismetMathLibrary.Abs_Int;
+KismetMathLibrary.prototype.Absolute = KismetMathLibrary.prototype.Abs;
+KismetMathLibrary.Absolute = KismetMathLibrary.Abs;
+KismetStringLibrary.prototype.NotEqual = KismetStringLibrary.prototype.NotEqual_StrStr;
+KismetStringLibrary.NotEqual = KismetStringLibrary.NotEqual_StrStr;
+KismetStringLibrary.prototype.Equal = KismetStringLibrary.prototype.EqualEqual_StrStr;
+KismetStringLibrary.Equal = KismetStringLibrary.EqualEqual_StrStr;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_VectorToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_VectorToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_Vector2dToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_Vector2dToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_TransformToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_TransformToString;
+KismetStringLibrary.prototype.StringToName = KismetStringLibrary.prototype.Conv_StringToName;
+KismetStringLibrary.StringToName = KismetStringLibrary.Conv_StringToName;
+KismetStringLibrary.prototype.StringToInt = KismetStringLibrary.prototype.Conv_StringToInt;
+KismetStringLibrary.StringToInt = KismetStringLibrary.Conv_StringToInt;
+KismetStringLibrary.prototype.StringToFloat = KismetStringLibrary.prototype.Conv_StringToFloat;
+KismetStringLibrary.StringToFloat = KismetStringLibrary.Conv_StringToFloat;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_RotatorToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_RotatorToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_ObjectToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_ObjectToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_NameToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_NameToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_IntToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_IntToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_FloatToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_FloatToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_ColorToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_ColorToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_ByteToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_ByteToString;
+KismetStringLibrary.prototype.ToString = KismetStringLibrary.prototype.Conv_BoolToString;
+KismetStringLibrary.ToString = KismetStringLibrary.Conv_BoolToString;
+KismetStringLibrary.prototype.Append = KismetStringLibrary.prototype.Concat_StrStr;
+KismetStringLibrary.Append = KismetStringLibrary.Concat_StrStr;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Vector2d;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Vector2d;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Vector;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Vector;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Rotator;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Rotator;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Object;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Object;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Name;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Name;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Int;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Int;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Float;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Float;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Color;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Color;
+KismetStringLibrary.prototype.BuildString = KismetStringLibrary.prototype.BuildString_Bool;
+KismetStringLibrary.BuildString = KismetStringLibrary.BuildString_Bool;
+KismetSystemLibrary.prototype.SphereTraceForObjects = KismetSystemLibrary.prototype.SphereTraceSingleForObjects;
+KismetSystemLibrary.SphereTraceForObjects = KismetSystemLibrary.SphereTraceSingleForObjects;
+KismetSystemLibrary.prototype.SingleSphereTraceByObjectDeprecated = KismetSystemLibrary.prototype.SphereTraceSingleByObject_DEPRECATED;
+KismetSystemLibrary.SingleSphereTraceByObjectDeprecated = KismetSystemLibrary.SphereTraceSingleByObject_DEPRECATED;
+KismetSystemLibrary.prototype.SphereTraceByChannel = KismetSystemLibrary.prototype.SphereTraceSingle_NEW;
+KismetSystemLibrary.SphereTraceByChannel = KismetSystemLibrary.SphereTraceSingle_NEW;
+KismetSystemLibrary.prototype.SingleSphereTraceByChannelDeprecated = KismetSystemLibrary.prototype.SphereTraceSingle_DEPRECATED;
+KismetSystemLibrary.SingleSphereTraceByChannelDeprecated = KismetSystemLibrary.SphereTraceSingle_DEPRECATED;
+KismetSystemLibrary.prototype.MultiSphereTraceForObjects = KismetSystemLibrary.prototype.SphereTraceMultiForObjects;
+KismetSystemLibrary.MultiSphereTraceForObjects = KismetSystemLibrary.SphereTraceMultiForObjects;
+KismetSystemLibrary.prototype.MultiSphereTraceByObjectDeprecated = KismetSystemLibrary.prototype.SphereTraceMultiByObject_DEPRECATED;
+KismetSystemLibrary.MultiSphereTraceByObjectDeprecated = KismetSystemLibrary.SphereTraceMultiByObject_DEPRECATED;
+KismetSystemLibrary.prototype.MultiSphereTraceByChannel = KismetSystemLibrary.prototype.SphereTraceMulti_NEW;
+KismetSystemLibrary.MultiSphereTraceByChannel = KismetSystemLibrary.SphereTraceMulti_NEW;
+KismetSystemLibrary.prototype.MultiSphereTraceByChannelDeprecated = KismetSystemLibrary.prototype.SphereTraceMulti_DEPRECATED;
+KismetSystemLibrary.MultiSphereTraceByChannelDeprecated = KismetSystemLibrary.SphereTraceMulti_DEPRECATED;
+KismetSystemLibrary.prototype.SphereOverlapComponents = KismetSystemLibrary.prototype.SphereOverlapComponents_NEW;
+KismetSystemLibrary.SphereOverlapComponents = KismetSystemLibrary.SphereOverlapComponents_NEW;
+KismetSystemLibrary.prototype.SphereOverlapActors = KismetSystemLibrary.prototype.SphereOverlapActors_NEW;
+KismetSystemLibrary.SphereOverlapActors = KismetSystemLibrary.SphereOverlapActors_NEW;
+KismetSystemLibrary.prototype.LineTraceForObjects = KismetSystemLibrary.prototype.LineTraceSingleForObjects;
+KismetSystemLibrary.LineTraceForObjects = KismetSystemLibrary.LineTraceSingleForObjects;
+KismetSystemLibrary.prototype.SingleLineTraceByObjectDeprecated = KismetSystemLibrary.prototype.LineTraceSingleByObject_DEPRECATED;
+KismetSystemLibrary.SingleLineTraceByObjectDeprecated = KismetSystemLibrary.LineTraceSingleByObject_DEPRECATED;
+KismetSystemLibrary.prototype.LineTraceByChannel = KismetSystemLibrary.prototype.LineTraceSingle_NEW;
+KismetSystemLibrary.LineTraceByChannel = KismetSystemLibrary.LineTraceSingle_NEW;
+KismetSystemLibrary.prototype.SingleLineTraceByChannelDeprecated = KismetSystemLibrary.prototype.LineTraceSingle_DEPRECATED;
+KismetSystemLibrary.SingleLineTraceByChannelDeprecated = KismetSystemLibrary.LineTraceSingle_DEPRECATED;
+KismetSystemLibrary.prototype.MultiLineTraceForObjects = KismetSystemLibrary.prototype.LineTraceMultiForObjects;
+KismetSystemLibrary.MultiLineTraceForObjects = KismetSystemLibrary.LineTraceMultiForObjects;
+KismetSystemLibrary.prototype.MultiLineTraceByObjectDeprecated = KismetSystemLibrary.prototype.LineTraceMultiByObject_DEPRECATED;
+KismetSystemLibrary.MultiLineTraceByObjectDeprecated = KismetSystemLibrary.LineTraceMultiByObject_DEPRECATED;
+KismetSystemLibrary.prototype.MultiLineTraceByChannel = KismetSystemLibrary.prototype.LineTraceMulti_NEW;
+KismetSystemLibrary.MultiLineTraceByChannel = KismetSystemLibrary.LineTraceMulti_NEW;
+KismetSystemLibrary.prototype.MultiLineTraceByChannelDeprecated = KismetSystemLibrary.prototype.LineTraceMulti_DEPRECATED;
+KismetSystemLibrary.MultiLineTraceByChannelDeprecated = KismetSystemLibrary.LineTraceMulti_DEPRECATED;
+KismetSystemLibrary.prototype.UnpauseTimerbyHandle = KismetSystemLibrary.prototype.K2_UnPauseTimerHandle;
+KismetSystemLibrary.UnpauseTimerbyHandle = KismetSystemLibrary.K2_UnPauseTimerHandle;
+KismetSystemLibrary.prototype.UnpauseTimerbyFunctionName = KismetSystemLibrary.prototype.K2_UnPauseTimer;
+KismetSystemLibrary.UnpauseTimerbyFunctionName = KismetSystemLibrary.K2_UnPauseTimer;
+KismetSystemLibrary.prototype.DoesTimerExistbyHandle = KismetSystemLibrary.prototype.K2_TimerExistsHandle;
+KismetSystemLibrary.DoesTimerExistbyHandle = KismetSystemLibrary.K2_TimerExistsHandle;
+KismetSystemLibrary.prototype.DoesTimerExistbyFunctionName = KismetSystemLibrary.prototype.K2_TimerExists;
+KismetSystemLibrary.DoesTimerExistbyFunctionName = KismetSystemLibrary.K2_TimerExists;
+KismetSystemLibrary.prototype.SetTimerbyFunctionName = KismetSystemLibrary.prototype.K2_SetTimer;
+KismetSystemLibrary.SetTimerbyFunctionName = KismetSystemLibrary.K2_SetTimer;
+KismetSystemLibrary.prototype.PauseTimerbyHandle = KismetSystemLibrary.prototype.K2_PauseTimerHandle;
+KismetSystemLibrary.PauseTimerbyHandle = KismetSystemLibrary.K2_PauseTimerHandle;
+KismetSystemLibrary.prototype.PauseTimerbyFunctionName = KismetSystemLibrary.prototype.K2_PauseTimer;
+KismetSystemLibrary.PauseTimerbyFunctionName = KismetSystemLibrary.K2_PauseTimer;
+KismetSystemLibrary.prototype.IsValid = KismetSystemLibrary.prototype.K2_IsValidTimerHandle;
+KismetSystemLibrary.IsValid = KismetSystemLibrary.K2_IsValidTimerHandle;
+KismetSystemLibrary.prototype.IsTimerPausedbyHandle = KismetSystemLibrary.prototype.K2_IsTimerPausedHandle;
+KismetSystemLibrary.IsTimerPausedbyHandle = KismetSystemLibrary.K2_IsTimerPausedHandle;
+KismetSystemLibrary.prototype.IsTimerPausedbyFunctionName = KismetSystemLibrary.prototype.K2_IsTimerPaused;
+KismetSystemLibrary.IsTimerPausedbyFunctionName = KismetSystemLibrary.K2_IsTimerPaused;
+KismetSystemLibrary.prototype.IsTimerActivebyHandle = KismetSystemLibrary.prototype.K2_IsTimerActiveHandle;
+KismetSystemLibrary.IsTimerActivebyHandle = KismetSystemLibrary.K2_IsTimerActiveHandle;
+KismetSystemLibrary.prototype.IsTimerActivebyFunctionName = KismetSystemLibrary.prototype.K2_IsTimerActive;
+KismetSystemLibrary.IsTimerActivebyFunctionName = KismetSystemLibrary.K2_IsTimerActive;
+KismetSystemLibrary.prototype.Invalidate = KismetSystemLibrary.prototype.K2_InvalidateTimerHandle;
+KismetSystemLibrary.Invalidate = KismetSystemLibrary.K2_InvalidateTimerHandle;
+KismetSystemLibrary.prototype.GetTimerRemainingTimebyHandle = KismetSystemLibrary.prototype.K2_GetTimerRemainingTimeHandle;
+KismetSystemLibrary.GetTimerRemainingTimebyHandle = KismetSystemLibrary.K2_GetTimerRemainingTimeHandle;
+KismetSystemLibrary.prototype.GetTimerRemainingTimebyFunctionName = KismetSystemLibrary.prototype.K2_GetTimerRemainingTime;
+KismetSystemLibrary.GetTimerRemainingTimebyFunctionName = KismetSystemLibrary.K2_GetTimerRemainingTime;
+KismetSystemLibrary.prototype.GetTimerElapsedTimebyHandle = KismetSystemLibrary.prototype.K2_GetTimerElapsedTimeHandle;
+KismetSystemLibrary.GetTimerElapsedTimebyHandle = KismetSystemLibrary.K2_GetTimerElapsedTimeHandle;
+KismetSystemLibrary.prototype.GetTimerElapsedTimebyFunctionName = KismetSystemLibrary.prototype.K2_GetTimerElapsedTime;
+KismetSystemLibrary.GetTimerElapsedTimebyFunctionName = KismetSystemLibrary.K2_GetTimerElapsedTime;
+KismetSystemLibrary.prototype.ClearTimerbyHandle = KismetSystemLibrary.prototype.K2_ClearTimerHandle;
+KismetSystemLibrary.ClearTimerbyHandle = KismetSystemLibrary.K2_ClearTimerHandle;
+KismetSystemLibrary.prototype.ClearTimerbyFunctionName = KismetSystemLibrary.prototype.K2_ClearTimer;
+KismetSystemLibrary.ClearTimerbyFunctionName = KismetSystemLibrary.K2_ClearTimer;
+KismetSystemLibrary.prototype.GetDisplayName = KismetSystemLibrary.prototype.GetClassDisplayName;
+KismetSystemLibrary.GetDisplayName = KismetSystemLibrary.GetClassDisplayName;
+KismetSystemLibrary.prototype.DrawDebugCone = KismetSystemLibrary.prototype.DrawDebugConeInDegrees;
+KismetSystemLibrary.DrawDebugCone = KismetSystemLibrary.DrawDebugConeInDegrees;
+KismetSystemLibrary.prototype.ComponentOverlapComponents = KismetSystemLibrary.prototype.ComponentOverlapComponents_NEW;
+KismetSystemLibrary.ComponentOverlapComponents = KismetSystemLibrary.ComponentOverlapComponents_NEW;
+KismetSystemLibrary.prototype.ComponentOverlapActors = KismetSystemLibrary.prototype.ComponentOverlapActors_NEW;
+KismetSystemLibrary.ComponentOverlapActors = KismetSystemLibrary.ComponentOverlapActors_NEW;
+KismetSystemLibrary.prototype.CapsuleTraceForObjects = KismetSystemLibrary.prototype.CapsuleTraceSingleForObjects;
+KismetSystemLibrary.CapsuleTraceForObjects = KismetSystemLibrary.CapsuleTraceSingleForObjects;
+KismetSystemLibrary.prototype.SingleCapsuleTraceByObjectDeprecated = KismetSystemLibrary.prototype.CapsuleTraceSingleByObject_DEPRECATED;
+KismetSystemLibrary.SingleCapsuleTraceByObjectDeprecated = KismetSystemLibrary.CapsuleTraceSingleByObject_DEPRECATED;
+KismetSystemLibrary.prototype.CapsuleTraceByChannel = KismetSystemLibrary.prototype.CapsuleTraceSingle_NEW;
+KismetSystemLibrary.CapsuleTraceByChannel = KismetSystemLibrary.CapsuleTraceSingle_NEW;
+KismetSystemLibrary.prototype.SingleCapsuleTraceByChannelDeprecated = KismetSystemLibrary.prototype.CapsuleTraceSingle_DEPRECATED;
+KismetSystemLibrary.SingleCapsuleTraceByChannelDeprecated = KismetSystemLibrary.CapsuleTraceSingle_DEPRECATED;
+KismetSystemLibrary.prototype.MultiCapsuleTraceForObjects = KismetSystemLibrary.prototype.CapsuleTraceMultiForObjects;
+KismetSystemLibrary.MultiCapsuleTraceForObjects = KismetSystemLibrary.CapsuleTraceMultiForObjects;
+KismetSystemLibrary.prototype.MultiCapsuleTraceByObjectDeprecated = KismetSystemLibrary.prototype.CapsuleTraceMultiByObject_DEPRECATED;
+KismetSystemLibrary.MultiCapsuleTraceByObjectDeprecated = KismetSystemLibrary.CapsuleTraceMultiByObject_DEPRECATED;
+KismetSystemLibrary.prototype.MultiCapsuleTraceByChannel = KismetSystemLibrary.prototype.CapsuleTraceMulti_NEW;
+KismetSystemLibrary.MultiCapsuleTraceByChannel = KismetSystemLibrary.CapsuleTraceMulti_NEW;
+KismetSystemLibrary.prototype.MultiCapsuleTraceByChannelDeprecated = KismetSystemLibrary.prototype.CapsuleTraceMulti_DEPRECATED;
+KismetSystemLibrary.MultiCapsuleTraceByChannelDeprecated = KismetSystemLibrary.CapsuleTraceMulti_DEPRECATED;
+KismetSystemLibrary.prototype.CapsuleOverlapComponents = KismetSystemLibrary.prototype.CapsuleOverlapComponents_NEW;
+KismetSystemLibrary.CapsuleOverlapComponents = KismetSystemLibrary.CapsuleOverlapComponents_NEW;
+KismetSystemLibrary.prototype.CapsuleOverlapActors = KismetSystemLibrary.prototype.CapsuleOverlapActors_NEW;
+KismetSystemLibrary.CapsuleOverlapActors = KismetSystemLibrary.CapsuleOverlapActors_NEW;
+KismetSystemLibrary.prototype.BoxTraceForObjects = KismetSystemLibrary.prototype.BoxTraceSingleForObjects;
+KismetSystemLibrary.BoxTraceForObjects = KismetSystemLibrary.BoxTraceSingleForObjects;
+KismetSystemLibrary.prototype.BoxTraceByChannel = KismetSystemLibrary.prototype.BoxTraceSingle;
+KismetSystemLibrary.BoxTraceByChannel = KismetSystemLibrary.BoxTraceSingle;
+KismetSystemLibrary.prototype.MultiBoxTraceForObjects = KismetSystemLibrary.prototype.BoxTraceMultiForObjects;
+KismetSystemLibrary.MultiBoxTraceForObjects = KismetSystemLibrary.BoxTraceMultiForObjects;
+KismetSystemLibrary.prototype.MultiBoxTraceByChannel = KismetSystemLibrary.prototype.BoxTraceMulti;
+KismetSystemLibrary.MultiBoxTraceByChannel = KismetSystemLibrary.BoxTraceMulti;
+KismetSystemLibrary.prototype.BoxOverlapComponents = KismetSystemLibrary.prototype.BoxOverlapComponents_NEW;
+KismetSystemLibrary.BoxOverlapComponents = KismetSystemLibrary.BoxOverlapComponents_NEW;
+KismetSystemLibrary.prototype.BoxOverlapActors = KismetSystemLibrary.prototype.BoxOverlapActors_NEW;
+KismetSystemLibrary.BoxOverlapActors = KismetSystemLibrary.BoxOverlapActors_NEW;
+KismetTextLibrary.prototype.NotEqual = KismetTextLibrary.prototype.NotEqual_TextText;
+KismetTextLibrary.NotEqual = KismetTextLibrary.NotEqual_TextText;
+KismetTextLibrary.prototype.Equal = KismetTextLibrary.prototype.EqualEqual_TextText;
+KismetTextLibrary.Equal = KismetTextLibrary.EqualEqual_TextText;
+KismetTextLibrary.prototype.ToString = KismetTextLibrary.prototype.Conv_TextToString;
+KismetTextLibrary.ToString = KismetTextLibrary.Conv_TextToString;
+KismetTextLibrary.prototype.ToText = KismetTextLibrary.prototype.Conv_StringToText;
+KismetTextLibrary.ToText = KismetTextLibrary.Conv_StringToText;
+KismetTextLibrary.prototype.ToText = KismetTextLibrary.prototype.Conv_NameToText;
+KismetTextLibrary.ToText = KismetTextLibrary.Conv_NameToText;
+KismetTextLibrary.prototype.ToText = KismetTextLibrary.prototype.Conv_IntToText;
+KismetTextLibrary.ToText = KismetTextLibrary.Conv_IntToText;
+KismetTextLibrary.prototype.ToText = KismetTextLibrary.prototype.Conv_FloatToText;
+KismetTextLibrary.ToText = KismetTextLibrary.Conv_FloatToText;
+KismetTextLibrary.prototype.ToText = KismetTextLibrary.prototype.Conv_ByteToText;
+KismetTextLibrary.ToText = KismetTextLibrary.Conv_ByteToText;
+KismetTextLibrary.prototype.ToText = KismetTextLibrary.prototype.Conv_BoolToText;
+KismetTextLibrary.ToText = KismetTextLibrary.Conv_BoolToText;
+KismetTextLibrary.prototype.AsTimespan = KismetTextLibrary.prototype.AsTimespan_Timespan;
+KismetTextLibrary.AsTimespan = KismetTextLibrary.AsTimespan_Timespan;
+KismetTextLibrary.prototype.AsTime = KismetTextLibrary.prototype.AsTime_DateTime;
+KismetTextLibrary.AsTime = KismetTextLibrary.AsTime_DateTime;
+KismetTextLibrary.prototype.AsPercent = KismetTextLibrary.prototype.AsPercent_Float;
+KismetTextLibrary.AsPercent = KismetTextLibrary.AsPercent_Float;
+KismetTextLibrary.prototype.AsDateTime = KismetTextLibrary.prototype.AsDateTime_DateTime;
+KismetTextLibrary.AsDateTime = KismetTextLibrary.AsDateTime_DateTime;
+KismetTextLibrary.prototype.AsDate = KismetTextLibrary.prototype.AsDate_DateTime;
+KismetTextLibrary.AsDate = KismetTextLibrary.AsDate_DateTime;
+KismetTextLibrary.prototype.AsCurrency = KismetTextLibrary.prototype.AsCurrency_Integer;
+KismetTextLibrary.AsCurrency = KismetTextLibrary.AsCurrency_Integer;
+KismetTextLibrary.prototype.AsCurrency = KismetTextLibrary.prototype.AsCurrency_Float;
+KismetTextLibrary.AsCurrency = KismetTextLibrary.AsCurrency_Float;
+VisualLoggerKismetLibrary.prototype.LogBoxShape = VisualLoggerKismetLibrary.prototype.LogBox;
+VisualLoggerKismetLibrary.LogBoxShape = VisualLoggerKismetLibrary.LogBox;
+Canvas.prototype.ClippedTextSize = Canvas.prototype.K2_TextSize;
+Canvas.prototype.WrappedTextSize = Canvas.prototype.K2_StrLen;
+Canvas.prototype.Project = Canvas.prototype.K2_Project;
+Canvas.prototype.DrawTriangles = Canvas.prototype.K2_DrawTriangle;
+Canvas.prototype.DrawTexture = Canvas.prototype.K2_DrawTexture;
+Canvas.prototype.DrawText = Canvas.prototype.K2_DrawText;
+Canvas.prototype.DrawPolygon = Canvas.prototype.K2_DrawPolygon;
+Canvas.prototype.DrawMaterialTriangles = Canvas.prototype.K2_DrawMaterialTriangle;
+Canvas.prototype.DrawMaterial = Canvas.prototype.K2_DrawMaterial;
+Canvas.prototype.DrawLine = Canvas.prototype.K2_DrawLine;
+Canvas.prototype.DrawBox = Canvas.prototype.K2_DrawBox;
+Canvas.prototype.DrawBorder = Canvas.prototype.K2_DrawBorder;
+Canvas.prototype.Deproject = Canvas.prototype.K2_Deproject;
+MaterialInstanceDynamic.prototype.InterpolateMaterialInstanceParameters = MaterialInstanceDynamic.prototype.K2_InterpolateMaterialInstanceParams;
+MaterialInstanceDynamic.prototype.GetVectorParameterValue = MaterialInstanceDynamic.prototype.K2_GetVectorParameterValue;
+MaterialInstanceDynamic.prototype.GetTextureParameterValue = MaterialInstanceDynamic.prototype.K2_GetTextureParameterValue;
+MaterialInstanceDynamic.prototype.GetScalarParameterValue = MaterialInstanceDynamic.prototype.K2_GetScalarParameterValue;
+MaterialInstanceDynamic.prototype.CopyMaterialInstanceParameters = MaterialInstanceDynamic.prototype.K2_CopyMaterialInstanceParameters;
+Texture2D.prototype.GetSizeY = Texture2D.prototype.Blueprint_GetSizeY;
+Texture2D.prototype.GetSizeX = Texture2D.prototype.Blueprint_GetSizeX;
+InAppPurchaseQueryCallbackProxy.prototype.ReadInAppPurchaseInformation = InAppPurchaseQueryCallbackProxy.prototype.CreateProxyObjectForInAppPurchaseQuery;
+InAppPurchaseQueryCallbackProxy.ReadInAppPurchaseInformation = InAppPurchaseQueryCallbackProxy.CreateProxyObjectForInAppPurchaseQuery;
+LeaderboardQueryCallbackProxy.prototype.ReadLeaderboardInteger = LeaderboardQueryCallbackProxy.prototype.CreateProxyObjectForIntQuery;
+LeaderboardQueryCallbackProxy.ReadLeaderboardInteger = LeaderboardQueryCallbackProxy.CreateProxyObjectForIntQuery;
+SubstanceGraphInstance.prototype.SetGraphInstanceOutputSize = SubstanceGraphInstance.prototype.SetGraphInstanceOutputSizeInt;
+SubstanceUtility.prototype.SetGraphInstanceOutputSize = SubstanceUtility.prototype.SetGraphInstanceOutputSizeInt;
+SubstanceUtility.SetGraphInstanceOutputSize = SubstanceUtility.SetGraphInstanceOutputSizeInt;
diff --git a/Examples/Content/Scripts/bootstrap.js b/Examples/Content/Scripts/bootstrap.js
new file mode 100644
index 0000000..581c4d0
--- /dev/null
+++ b/Examples/Content/Scripts/bootstrap.js
@@ -0,0 +1,14 @@
+(function (global) {
+	"use strict"
+	
+	module.exports = function (filename) {
+//		Context.WriteDTS(Context.Paths[0] + 'typings/ue.d.ts')
+//		Context.WriteAliases(Context.Paths[0] + 'aliases.js')
+
+		Context.RunFile('aliases.js')
+		Context.RunFile('polyfill/unrealengine.js')
+		Context.RunFile('polyfill/timers.js')
+		
+		require('devrequire')(filename)
+	}
+})(this)
\ No newline at end of file
diff --git a/Examples/Content/Scripts/devjade.js b/Examples/Content/Scripts/devjade.js
new file mode 100644
index 0000000..e8f223f
--- /dev/null
+++ b/Examples/Content/Scripts/devjade.js
@@ -0,0 +1,52 @@
+(function(){
+	"use strict";
+	
+	let UMG = require('UMG');	
+	let devrequire = require('devrequire');			
+	
+	function devjade(path,setup) {
+		function test() {
+			let design = require('jade-umg')(UMG,path)
+			return setup(design)
+		}
+		
+		function livereload(path,test) {
+			let files = [path]        
+			let fullpath = Context.GetScriptFileFullPath(path)
+			let cleanup = null
+			
+			let cleanup_devrequire = devrequire({
+				get_change : function (watcher) {
+					let full_files = files.map(function (x) { return Context.GetScriptFileFullPath(x)})
+					return full_files.filter(function (x) {return watcher.Contains(x)})                
+				},
+				exec : function () {
+					var design = test()
+					files = design.$files
+					cleanup = function() {
+						cleanup = null
+						design()
+					}
+					return cleanup
+				},
+				notify : true,
+				message : "Live reload (jade)"
+			})
+			
+			return function () {
+				cleanup_devrequire()
+				if (cleanup) {
+					cleanup()
+				}
+			}
+		}    
+		
+		var live_jade = livereload(path,function () {
+			return test()
+		})
+		
+		return live_jade				
+	}
+	
+	module.exports = devjade;
+})()
\ No newline at end of file
diff --git a/Examples/Content/Scripts/hello2048.js b/Examples/Content/Scripts/hello2048.js
new file mode 100644
index 0000000..b1fafe5
--- /dev/null
+++ b/Examples/Content/Scripts/hello2048.js
@@ -0,0 +1,18 @@
+/// <reference path="typings/ue.d.ts">/>
+(function (global) {	
+	"use strict"
+	 
+    try {
+        let main = require('2048')        
+        module.exports = () => {
+            let cleanup = null
+            process.nextTick(() => {
+                cleanup = main()               
+			});
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('hello2048')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloBlueprint.js b/Examples/Content/Scripts/helloBlueprint.js
new file mode 100644
index 0000000..20128e8
--- /dev/null
+++ b/Examples/Content/Scripts/helloBlueprint.js
@@ -0,0 +1,71 @@
+/// <reference path="typings/ue.d.ts">/>
+
+(function (global) {	
+	"use strict"
+	
+	class MyActor extends Blueprint.Load('/Game/ExampleBlueprint').GeneratedClass {
+		// constructor
+		ctor() {
+			// Subobject initialization, property initialization 
+			this.bAlwaysRelevant = true
+		}
+		
+		// declare UPROPERTY's here
+		// ; this.XXXXX/*[attribute+]+type*/;
+		properties() {
+			this.Hello/*Replicated+EditAnywhere+int*/;
+			this.World/*Replicated+EditAnywhere+Actor*/;
+			this.Position/*EditAnywhere+Vector[]*/;
+			this.Some/*EditAnywhere+DistanceDatum*/;    
+		}
+		
+		// Overriding function doesn't need function signature
+		ReceiveBeginPlay() {
+			super.ReceiveBeginPlay()
+			
+			console.log("Hello, this is MyActor") 
+		}
+		
+		// Override an event which was declared in Blueprint
+		CustomEvent() {
+			console.log("This is javascript")
+		}
+		
+		// New UFUNCTION needs proper function signature
+		// ; function-name(arg/*type*/,...) /*UFUNCTION-flag[+another flag]*/
+		NewFunction(x/*int*/,y/*int*/) /*NetMulticast*/ {
+			console.log(x+y);
+		}
+	}
+		
+	let MyActor_C = require('uclass')()(global,MyActor)
+	
+	let _ = require('lodash')	
+	
+	function GetPC() {
+		return GWorld.GetAllActorsOfClass(PlayerController).OutActors[0]
+	}
+	
+	function main() {
+		if (GWorld.IsServer()) {
+			let actor = new MyActor_C(GWorld,{X:1})					
+			
+			return function () {
+				actor.DestroyActor()
+			}
+		} else {
+			return function() {}
+		}				
+	}
+	 
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloBlueprint')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloCanvas.js b/Examples/Content/Scripts/helloCanvas.js
new file mode 100644
index 0000000..69be425
--- /dev/null
+++ b/Examples/Content/Scripts/helloCanvas.js
@@ -0,0 +1,40 @@
+(function (global) {
+	"use strict"
+	
+	function GetPC() {
+		return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0])
+	}
+	
+	function main() {
+		GetPC().DefaultMouseCursor = Symbol('Default') 
+				
+		class MyHUD extends HUD {
+			ctor() {				
+			}
+			
+			ReceiveDrawHUD() {
+				super.ReceiveDrawHUD()
+				
+				let text = `Hello Canvas : ${new Date().toISOString()}`
+				this.Canvas.DrawText(GEngine.SmallFont,text,{X:this.Canvas.SizeX/2,Y:this.Canvas.SizeY/2},{R:1,G:1,B:1,A:1},0,{R:0,G:0,B:0,A:1},{X:1,Y:1},true,true,true,{R:0,G:0,B:0,A:1})
+			}
+		}
+		
+		let MyHUD_C = require('uclass')()(global,MyHUD);			
+		GetPC().ClientSetHUD(MyHUD_C)		
+		
+		return function () {				
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloCanvas')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloCharacter.js b/Examples/Content/Scripts/helloCharacter.js
new file mode 100644
index 0000000..53fb424
--- /dev/null
+++ b/Examples/Content/Scripts/helloCharacter.js
@@ -0,0 +1,93 @@
+(function (global) {
+	"use strict"
+	
+	let _ = require('lodash')
+	
+	function GetPC() {
+		return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0])
+	}
+	
+	function main() {		
+		let PC = GetPC()
+		
+		class MyCharacter extends Character {
+			ctor() {		
+				let movement = this.GetMovementComponent() 		
+				movement.bUseRVOAvoidance = true
+				movement.bOrientRotationToMovement = true
+				this.bUseControllerRotationYaw = false				
+								
+				this.Mesh.AddLocalOffset({Y:0.000856,Z:-97})
+				this.Mesh.AddLocalRotation({Yaw:270})
+				this.Mesh.SetSkeletalMesh(SkeletalMesh.Load('/Game/Mannequin/Character/Mesh/SK_Mannequin.SK_Mannequin'))
+				this.Mesh.SetAnimInstanceClass(AnimBlueprint.Load('/Game/Mannequin/Animations/ThirdPerson_AnimBP.ThirdPerson_AnimBP').GeneratedClass)
+			}
+		}
+		
+		let MyCharacter_C = require('uclass')()(global,MyCharacter)
+		
+		function randomPoint() {
+			return GWorld.GetRandomPoint(GWorld.NavigationSystem.MainNavData)
+		}		
+		
+		function createCharacter() {
+			let pos = randomPoint()
+			pos.Z += 100
+			let character = Character.C(new MyCharacter_C(GWorld,pos))
+			character.SpawnDefaultController()
+			let controller = AIController.C(character.Controller)
+			
+			function wander() {									
+				controller.MoveToLocation(randomPoint(),-1,true,true,false,false)
+			}
+			
+			controller.ReceiveMoveCompleted.Add( (reqid,status) => {					
+				wander()
+			})
+			
+			wander()
+			return character
+		}
+		
+		let characters = _.range(0,20).map(()=>{
+			while (true) {
+				try {
+					return createCharacter()				
+				} 
+				catch (e) {
+				}
+			}
+		})
+		
+		let alive = true
+		
+		function tick() {
+			if (!alive) return;			
+			
+			characters = _.filter(characters,(c) => c.IsValid())
+			let sum = _.reduce(characters,(sum,c) => KismetMathLibrary.Add_VectorVector(sum,c.GetActorLocation()),{});			 		
+			let center = KismetMathLibrary.Divide_VectorInt(sum,characters.length)
+			PC.GetControlledPawn().SetActorLocation(KismetMathLibrary.Add_VectorVector(center,{Z:400}))				
+			PC.SetInitialLocationAndRotation({},{Yaw:(Date.now() / 100) % 360,Pitch:-40,Roll:0})				        			
+			
+			process.nextTick(tick);
+		}
+		tick()				
+		
+		return function () {
+			alive = false
+			characters.forEach((a) => a.DestroyActor())
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloCharacter')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloInputBinding.js b/Examples/Content/Scripts/helloInputBinding.js
new file mode 100644
index 0000000..546ef77
--- /dev/null
+++ b/Examples/Content/Scripts/helloInputBinding.js
@@ -0,0 +1,58 @@
+/// <reference path="typings/ue.d.ts">/>
+(function (global) {	
+	"use strict"	
+	
+	// To specify binding flag, pass '-flag' or '+flag' as a decorator.
+	// 
+	// eg) AxisBinding[Turn,-bConsumeInput]
+	//	
+	// FBlueprintInputDelegateBinding
+	// 	bConsumeInput, bExecuteWhenPaused, bOverrideParentBinding
+	
+	class MyPlayerController extends PlayerController  {
+		Wave() /*ActionBinding[Wave]*/ {
+			console.log("Wave!!")
+		}
+		
+		Jump() /*ActionBinding[Jump2,IE_Released]*/ {
+			console.log("Jump!!")
+		}
+		
+		Hi() /*KeyBinding[Ctrl+E]*/ {
+			console.log("Hi!")
+		}		
+		
+		Turn(value/*float*/) /*AxisBinding[Turn,-bConsumeInput]*/ {
+			if (value) {
+				console.log("Turn2",value)
+			}			
+		}
+	}
+	
+	function GetPC() {
+		return GWorld.GetAllActorsOfClass(PlayerController).OutActors[0]
+	}	
+	
+	// Switch PlayerController class to JS generated one.  
+	let MyPC_C = require('uclass')()(global,MyPlayerController)	
+	GWorld.GetGameMode().PlayerControllerClass = MyPC_C	
+	
+	if (GetPC()) {
+		GWorld.GetGameMode().HandleSeamlessTravelPlayer(GetPC())
+	}
+	
+	function main() {		
+		return () => {}
+	}	 
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloInputBinding')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloJade.js b/Examples/Content/Scripts/helloJade.js
new file mode 100644
index 0000000..8773a13
--- /dev/null
+++ b/Examples/Content/Scripts/helloJade.js
@@ -0,0 +1,51 @@
+(function (global) {
+	"use strict"
+	
+	function GetPC() {
+		return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0])
+	}
+	
+	function main() {
+		let cleanup = null
+		let PC = GetPC()
+		
+		function setup(design) {
+			let instantiator = require('instantiator');
+			let page = instantiator(design)				
+		
+			let widget = WidgetBlueprintLibrary.CreateWidget(GWorld, JavascriptWidget, PC)			
+			widget.JavascriptContext = Context    
+			widget.bSupportsKeyboardFocus = true
+		
+			page.Visibility = 'Visible'
+			widget.SetRootWidget(page)
+			widget.AddToViewport()
+			PC.SetInputMode_UIOnly(page)
+			PC.bShowMouseCursor = true
+			
+			let cleanup = function() {
+				widget.RemoveFromViewport()
+			}
+			cleanup.$files = design.$files
+			return cleanup
+		}
+		
+		let devjade = require('devjade')					 
+		cleanup = devjade('views/helloJade.jade',setup)
+		
+		return function () {			
+			cleanup()	
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloJade')	
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloProcess.js b/Examples/Content/Scripts/helloProcess.js
new file mode 100644
index 0000000..9bb3fb0
--- /dev/null
+++ b/Examples/Content/Scripts/helloProcess.js
@@ -0,0 +1,84 @@
+(function (global) {
+	"use strict"
+	
+	function GetPC() {
+		return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0])
+	}
+	
+	function makeDisplay() {
+		let text = ''
+		let appendText = (message) => {
+			if (message != '') {
+				text += message
+			}
+		} 
+		
+		class MyHUD extends HUD {
+			ctor() {				
+			}
+			
+			ReceiveDrawHUD() {
+				super.ReceiveDrawHUD()				
+				
+				this.Canvas.DrawText(GEngine.SmallFont,text,{X:this.Canvas.SizeX/2,Y:this.Canvas.SizeY/2},{R:1,G:1,B:1,A:1},0,{R:0,G:0,B:0,A:1},{X:1,Y:1},true,true,true,{R:0,G:0,B:0,A:1})
+			}
+		}
+		
+		let MyHUD_C = require('uclass')()(global,MyHUD);			
+		GetPC().ClientSetHUD(MyHUD_C)
+		
+		return appendText
+	}
+	
+	function main() {
+		let appendText = makeDisplay()	
+		
+		appendText("executing node.js:\n")
+		
+		function test() { 
+			[1,2,3,4,5].forEach(function(x,i) { 
+				setTimeout(function () { 
+					console.log('Node.js is speaking ' + x) 
+				}, i * 1000 ) 
+			} ) 
+		}
+		 
+		let alive = true
+		let proc = JavascriptProcess.Create('node',`-e "${String(test)}; test()"`,true,false,false,0,'',true)
+		if (proc) {
+			let kick = () => {			
+				appendText(proc.ReadFromPipe())
+							
+				if (proc.IsRunning()) {				
+					process.nextTick(kick)
+				} else {
+					appendText("<end of execution>")
+				}
+			}
+			
+			kick()			
+		} else {
+			appendText("failed to create process")
+		}
+		
+		return function () {
+			alive = false
+			if (proc) {
+				proc.Close()
+			}	
+		}								
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloProcess')	
+	}
+})(this)
+
+
diff --git a/Examples/Content/Scripts/helloReplication.js b/Examples/Content/Scripts/helloReplication.js
new file mode 100644
index 0000000..e4dbcce
--- /dev/null
+++ b/Examples/Content/Scripts/helloReplication.js
@@ -0,0 +1,72 @@
+/// <reference path="typings/ue.d.ts">/>
+(function (global) {	
+	"use strict"
+
+	class MyActor extends Actor {
+		ctor() {
+			this.bAlwaysRelevant = true
+		}
+		
+		properties() {
+			this.Hello/*Replicated+EditAnywhere+int*/;
+			this.World/*Replicated+EditAnywhere+Actor*/;
+			this.Position/*EditAnywhere+Vector*/;  
+		}
+		
+		ReceiveBeginPlay() {
+			super.ReceiveBeginPlay()
+			
+			console.log("Hello, this is MyActor")
+		}
+		
+		test(data/*float*/)/*NetMulticast*/ {
+			if (this.Role == 'ROLE_Authority') {
+				this.Hello++
+			} 					
+			console.log('multicast!',data,this.GetName(),this.Hello,this.Role)
+		}
+	}
+		
+	let MyActor_C = require('uclass')()(global,MyActor)
+	
+	Context.SetAsDebugContext()
+	
+	let _ = require('lodash')	
+	
+	function GetPC() {
+		return GWorld.GetAllActorsOfClass(PlayerController).OutActors[0]
+	}
+	function main() {
+		if (GWorld.IsServer()) {
+			let actor = new MyActor_C(GWorld,{X:1})
+			actor.SetReplicates(true)
+			
+			let alive = true
+			function kick() {
+				if (!alive) return				
+				actor.test(Math.random())
+				setTimeout(kick,1000)
+			}
+			
+			kick()			
+			
+			return function () {
+				alive = false
+				actor.DestroyActor()
+			}
+		} else {
+			return function() {}
+		}				
+	}
+	 
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloReplication')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloRest.js b/Examples/Content/Scripts/helloRest.js
new file mode 100644
index 0000000..05c5011
--- /dev/null
+++ b/Examples/Content/Scripts/helloRest.js
@@ -0,0 +1,31 @@
+(function (global) {
+	"use strict"
+	
+	function main() {
+		let actor = new TextRenderActor(GWorld,{X:100,Z:100},{Yaw:180})	
+		actor.TextRender.SetHorizontalAlignment('EHTA_Center') 
+		actor.TextRender.SetText('Hello Rest')
+		
+		let request = require('request')		
+		request("POST","http://localhost:3000",{data:{json:'hello'}}).then((json) => {
+			actor.TextRender.SetText(JSON.stringify(json))
+		}).catch((error) => {
+			actor.TextRender.SetText(error)
+		})
+		
+		return function () {
+			actor.DestroyActor()	
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloRest')	
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloScope.js b/Examples/Content/Scripts/helloScope.js
new file mode 100644
index 0000000..d629309
--- /dev/null
+++ b/Examples/Content/Scripts/helloScope.js
@@ -0,0 +1,89 @@
+(function (global) {
+	"use strict"
+	
+	let _ = require('lodash')
+	
+	function rootScope(scope) {
+		scope.text = 'Hello Scope!'
+		scope.count = 0
+		scope.inc = () => scope.count++
+		scope.items = []
+		scope.discard = (item) => {
+			scope.items = _.filter(scope.items,(i) => i != item)
+		}
+		scope.add = () => {			
+			scope.items.push({key:Math.random(),value:Math.random()})
+		}
+		_.range(0,10).forEach(()=>scope.add())
+	}
+	
+	function makePage(design) {
+		let UMG = require('UMG');
+		let Style = require('style');
+		
+		let page = UMG.app(design,rootScope)
+		let ES = JavascriptEditorStyle;
+				
+		page.set_styles([
+			Style('.full',{'slot.size.size-rule':'Fill'}),
+			Style('Button',{'WidgetStyle':ES.GetButtonStyle('FlatButton.Default')},
+				Style('TextBlock',{'font.size':53})
+			),
+			Style('TextBlock',{'font.size':43}),
+			Style('.yellow',{'color-and-opacity.specified-color':{R:1,G:1,B:0.2,A:1}}),
+			Style('.small',{'font.size':23},
+				Style('TextBlock',{'font.size':23})
+			),
+			Style('EditableText',{WidgetStyle:{'font.size':43}})
+		])
+		
+		return page
+	}
+	
+	function GetPC() {
+		return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0])
+	}
+	
+	function main() {
+		let cleanup = null		
+		let PC = GetPC()
+		
+		function setup(design) {
+			let page = makePage(design)								
+		
+			let widget = WidgetBlueprintLibrary.CreateWidget(GWorld, JavascriptWidget, PC)			
+			widget.JavascriptContext = Context    
+			widget.bSupportsKeyboardFocus = true
+		
+			page.Visibility = 'Visible'
+			widget.SetRootWidget(page)
+			widget.AddToViewport()
+			PC.SetInputMode_UIOnly(page)
+			PC.bShowMouseCursor = true
+			
+			let cleanup = function() {
+				widget.RemoveFromViewport()
+			}
+			cleanup.$files = design.$files
+			return cleanup
+		}
+		
+		let devjade = require('devjade')					 
+		cleanup = devjade('views/helloScope.jade',setup)
+		
+		return function () {			
+			cleanup()	
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloScope')	
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloTimer.js b/Examples/Content/Scripts/helloTimer.js
new file mode 100644
index 0000000..5394f6c
--- /dev/null
+++ b/Examples/Content/Scripts/helloTimer.js
@@ -0,0 +1,33 @@
+/// <reference path="typings/ue.d.ts">/>
+
+(function (global) {
+    "use strict"    
+	
+	function main() {
+		let actor = new TextRenderActor(GWorld,{X:100,Z:100},{Yaw:180})	
+		
+		let timer = null;
+		function update() {
+			actor.TextRender.SetText(`Hello Timer : ${new Date().toISOString()}`)
+			timer = setTimeout(update, 1000)			
+		}
+		
+		update()					
+		
+		return function () {
+			actor.DestroyActor()
+			clearTimeout(timer)	
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloTimer')	
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloUMG.js b/Examples/Content/Scripts/helloUMG.js
new file mode 100644
index 0000000..2928b2e
--- /dev/null
+++ b/Examples/Content/Scripts/helloUMG.js
@@ -0,0 +1,53 @@
+(function (global) {
+	"use strict"
+	
+	function GetPC() {
+		return PlayerController.C(GWorld.GetAllActorsOfClass(PlayerController).OutActors[0])
+	}
+	
+	function main() {
+		let widget = null		
+		let PC = GetPC()
+		
+		// Widget을 만들자
+		widget = WidgetBlueprintLibrary.CreateWidget(GWorld, JavascriptWidget, PC)			
+		widget.JavascriptContext = Context    
+		widget.bSupportsKeyboardFocus = true
+		
+		let UMG = require('UMG')
+		let design = UMG.span({},
+				UMG(Button,{'slot.size.size-rule':'Fill'},UMG.text({},"Hello")),
+				UMG.div({'slot.size.size-rule':'Fill'},
+					UMG(Button,{'slot.size.size-rule':'Fill'},UMG.text({},"UMG")),
+					UMG(Button,{'slot.size.size-rule':'Fill'},UMG.text({},"!!!!"))
+				)					
+		)
+		
+		let instantiator = require('instantiator')
+		let page = instantiator(design)
+		
+		page.Visibility = 'Visible'
+		
+		widget.SetRootWidget(page)
+		widget.AddToViewport()    
+	
+		// UIOnly mode로 설정    
+		PC.bShowMouseCursor = true
+		PC.SetInputMode_UIOnly(page)
+		
+		return function () {			
+			widget.RemoveFromViewport()	
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloUMG')
+	}
+})(this)
diff --git a/Examples/Content/Scripts/helloWorld.js b/Examples/Content/Scripts/helloWorld.js
new file mode 100644
index 0000000..3cb6074
--- /dev/null
+++ b/Examples/Content/Scripts/helloWorld.js
@@ -0,0 +1,24 @@
+(function (global) {
+	"use strict"
+	
+	function main() {
+		let actor = new TextRenderActor(GWorld,{X:100,Z:100},{Yaw:180})	
+		actor.TextRender.SetHorizontalAlignment('EHTA_Center') 
+		actor.TextRender.SetText('Hello World')					
+		
+		return function () {
+			actor.DestroyActor()	
+		}				
+	}
+	
+	try {
+		module.exports = () => {
+			let cleanup = null
+			process.nextTick(() => cleanup = main());
+			return () => cleanup()
+		}
+	}
+	catch (e) {
+		require('bootstrap')('helloWorld')	
+	}
+})(this)
diff --git a/Examples/Content/Scripts/jsconfig.json b/Examples/Content/Scripts/jsconfig.json
new file mode 100644
index 0000000..65ff2e4
--- /dev/null
+++ b/Examples/Content/Scripts/jsconfig.json
@@ -0,0 +1,8 @@
+{	
+	"compilerOptions": {
+		"target": "ES6",
+		"noLib": false,
+		"charset": "utf8",
+		"locale": "ko-kr"
+	}
+}
\ No newline at end of file
diff --git a/Examples/Content/Scripts/typings/_part_0_ue.d.ts b/Examples/Content/Scripts/typings/_part_0_ue.d.ts
new file mode 100644
index 0000000..15de2b8
Binary files /dev/null and b/Examples/Content/Scripts/typings/_part_0_ue.d.ts differ
diff --git a/Examples/Content/Scripts/typings/_part_1_ue.d.ts b/Examples/Content/Scripts/typings/_part_1_ue.d.ts
new file mode 100644
index 0000000..a39f5d5
Binary files /dev/null and b/Examples/Content/Scripts/typings/_part_1_ue.d.ts differ
diff --git a/Examples/Content/Scripts/typings/ue.d.ts b/Examples/Content/Scripts/typings/ue.d.ts
new file mode 100644
index 0000000..8a94ee0
Binary files /dev/null and b/Examples/Content/Scripts/typings/ue.d.ts differ
diff --git a/Examples/Content/Scripts/views/hello2048_design.js b/Examples/Content/Scripts/views/hello2048_design.js
new file mode 100644
index 0000000..d6fe99d
--- /dev/null
+++ b/Examples/Content/Scripts/views/hello2048_design.js
@@ -0,0 +1,68 @@
+function box_value(opts) {
+    return {      
+      slot : {
+        Padding : {Left:8,Right:8,Top:8,Bottom:8}
+      },
+      type : Border,
+      attrs : {
+        BrushColor : {R:0.5,G:0.5,B:0.2,A:0.2},
+        Padding : {Left:8,Right:8,Top:8,Bottom:8}
+      },
+      children : [ 
+        {
+          type : VerticalBox,
+          children : [
+            {            
+              type : TextBlock,
+              slot : { Size : { SizeRule : 'Fill' } },
+              attrs : {
+                Text : opts.label || '',
+                Justification : 'Center'
+              }
+            },
+            {            
+              id : opts.id,
+              type : TextBlock,
+              slot : { Size : { SizeRule : 'Fill' } },
+              attrs : {
+                Text : opts.value || '',
+                Justification : 'Center'
+              }
+            }
+          ]
+        }
+      ]
+    }
+    }
+
+module.exports = {
+    type : HorizontalBox,
+    children : [
+      {
+        slot : {
+          Padding : {Left:8, Right:8, Top:8, Bottom:8}
+        },
+        type : TextBlock,
+        attrs : {            
+          Text : '2048',
+          Font : {
+            Size : 90
+          }            
+        }            
+      },   
+      box_value({label:"Score",id:"score"}),
+      box_value({label:"Best",id:"best"}),
+      box_value({ label: "message", id: "message" }),
+      {
+          type: TextBlock,
+          slot: { Size: { SizeRule: 'Fill' } },
+          attrs: {
+              Text: 'github.com/gabrielecirculli/2048',
+              Justification: 'Right',
+              Font: {
+                  Size:40
+              }
+          }
+      }
+    ]
+}    
\ No newline at end of file
diff --git a/Examples/Content/Scripts/views/helloJade.jade b/Examples/Content/Scripts/views/helloJade.jade
new file mode 100644
index 0000000..a97a4ab
--- /dev/null
+++ b/Examples/Content/Scripts/views/helloJade.jade
@@ -0,0 +1,8 @@
+span
+	Button(slot.size.size-rule='Fill')
+		text Hello
+	div(slot.size.size-rule='Fill')
+		Button(slot.size.size-rule='Fill')
+			text Jade
+		Button(slot.size.size-rule='Fill')
+			text !!!!!
\ No newline at end of file
diff --git a/Examples/Content/Scripts/views/helloScope.jade b/Examples/Content/Scripts/views/helloScope.jade
new file mode 100644
index 0000000..7d04db4
--- /dev/null
+++ b/Examples/Content/Scripts/views/helloScope.jade
@@ -0,0 +1,17 @@
+div
+	span.full
+		Button.full
+			text {{text}}
+		div.full
+			Button.full(fn.on-clicked="inc()")
+				text {{count}}
+			Button.full(fn.on-clicked="add()")
+				text Click button above!
+	span
+		text.yellow >
+		EditableText(Binding.Text='text',fn.on-text-changed='text = ^arguments[0]',HintText="Your secret goes here")
+		
+	list.full(repeat='item in items',on-click="discard(item)") 
+		HorizontalBox.small
+			text.full {{item.key}}
+			text.full {{item.value}}
\ No newline at end of file
diff --git a/Examples/Content/ThirdPerson/Meshes/CubeMaterial.uasset b/Examples/Content/ThirdPerson/Meshes/CubeMaterial.uasset
new file mode 100644
index 0000000..d9351c3
--- /dev/null
+++ b/Examples/Content/ThirdPerson/Meshes/CubeMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3501f9bab29de026c45b69ae372a92668bb547a6d668d05ec250f6eaccd87eda
+size 491
diff --git a/Examples/JavascriptPlayground.uproject b/Examples/JavascriptPlayground.uproject
new file mode 100644
index 0000000..2c1645b
--- /dev/null
+++ b/Examples/JavascriptPlayground.uproject
@@ -0,0 +1,6 @@
+{
+	"FileVersion": 3,
+	"EngineAssociation": "{BF7CEC91-462B-B893-1CD4-668472DEC09D}",
+	"Category": "",
+	"Description": ""
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/.gitignore b/Plugins/UnrealJS/.gitignore
new file mode 100644
index 0000000..d1c54cf
--- /dev/null
+++ b/Plugins/UnrealJS/.gitignore
@@ -0,0 +1,3 @@
+Binaries
+Intermediate
+
diff --git a/Plugins/UnrealJS/Content/Scripts/UMG.js b/Plugins/UnrealJS/Content/Scripts/UMG.js
new file mode 100644
index 0000000..ad8cf13
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/UMG.js
@@ -0,0 +1,590 @@
+(function (global) {
+	"use strict"
+	
+	var scope_id = 0
+	
+	var _ = require('lodash')
+	var instantiator = require('instantiator')
+	var conv = instantiator.conv;
+	
+	var regex_interpolate = /\{\{[^\}]+\}\}/g;
+	
+	var split = require('css-split');	
+	
+	var UMG = {
+		app : function (template, root_scope) {
+			root_scope.$id = scope_id++
+			var model = {};
+			root_scope(model);
+			var page = null;
+			model.$apply = function () {
+				if (page) {
+					page.update()
+					page.sync()					
+				}
+			}
+			page = instantiator(template,model)
+			return page
+		},
+		text : function (opts,text) {
+			var out = {}
+			if (text) {
+				var arr = []
+				var last = 0
+				function push(t) {
+					arr.push('"'+t.replace('"','\"')+'"')
+				}
+				text.replace(regex_interpolate,function (match,pos) {					
+					push(text.substr(last,pos-last))
+					arr.push('"" + ('+match.substr(2,match.length-4)+')')
+					last = pos + match.length
+				});					
+				if (arr.length) {
+					push(text.substr(last));				
+					text = arr.join('+');
+					out['binding.text'] = text;
+				} else {
+					out.text = text;
+				}
+			}					
+			return this.generic(TextBlock, _.extend(out,opts))			
+		},
+		preview: function (opts) {		    
+			return UMG.base(Viewport,opts,{				
+				$link : function (elem,scope,attrs) {
+					var setup = instantiator.interpolate('handler',attrs.Setup);
+					process.nextTick(function() {
+						function ChildScope() {							
+						}						
+						ChildScope.prototype = scope
+						var child = new ChildScope()
+						child.$viewport = elem
+						setup && setup.call(child)
+						scope.$apply()
+					})				
+				}
+			})	
+		},
+		div : function (opts,array) {
+			return UMG.generic2(VerticalBox,opts,Array.prototype.slice.call(arguments,1))
+		},
+		span : function (opts,array) {
+			return UMG.generic2(HorizontalBox,opts,Array.prototype.slice.call(arguments,1))			
+		},
+		img : function (opts,array) {
+			return UMG.generic2(UImage,opts,Array.prototype.slice.call(arguments,1))
+		},
+		base : function (type, opts, other) {
+			var attrs = conv(opts)
+			var s = _.isString(type) ? split(type) : {}
+
+			// grab slot from attrs, and remove it!
+			var slot = attrs.Slot
+			delete attrs.Slot
+			
+			var id = attrs.Id
+			delete attrs.Id
+			
+			var klass = attrs.Class
+			if (klass) {				
+				if (_.isString(klass)) {
+					klass = klass.split(' ')
+				}				
+			}
+			delete attrs.Class
+			
+			if (s.type) type = s.type;
+			if (s.class) klass = s.class;
+			if (s.id) attrs.Id = s.id;
+					
+			return _.extend({
+				type : type,
+				id : id,
+				'class' : klass,
+				attrs : attrs,
+				slot : slot
+				}, other || {});
+		},
+		generic2 : function (type, opts, children) {
+			return UMG.base(type,opts,{
+				children : (children || []).map( function(c) {
+					return _.isString(c) ? UMG.text({},c) : c;
+				})
+			})			
+		},
+		generic : function (type, opts, template) {						
+			return UMG.generic2(type,opts,Array.prototype.slice.call(arguments,2))
+		},
+		controller : function (opts, template) {
+			return UMG.base(SizeBox,opts,{
+				$link : function (elem, scope, attrs) {
+					var fn = instantiator.interpolate('controller',attrs.Controller);
+					function ChildScopeClass() {}
+					ChildScopeClass.prototype = scope
+					var child_scope = new ChildScopeClass()
+					child_scope.$id = scope_id++
+					child_scope.$apply = function () {                        
+						elem.update()
+						elem.sync()
+					}
+					var controller = fn.call(scope)
+					controller(child_scope)					
+					
+					elem.add_child(template,child_scope)
+				}
+			})
+		},
+		sw : function (opts, template) {
+			if (_.isString(template)) template = UMG.text({},template) 
+			return UMG.base(SizeBox,_.extend({Visibility:'Collapsed'},opts),{
+				$link : function (elem, scope, attrs) {
+					var child = null
+					 
+					function set_data(flag) {						
+						if (flag && child == null) {
+							child = elem.add_child(template,scope)
+							elem.SetVisibility('Visible')																			
+						} else if (!flag && child) {
+							elem.remove_child(child)
+							elem.SetVisibility('Collapsed')
+							child = null							
+						}						
+					}
+										
+					var fn = instantiator.interpolate('sw',attrs.If);
+					set_data(fn.call(scope,true));
+						
+					elem.add_binding(function(instance,scope_instance) {
+						set_data(fn.call(scope_instance));
+					});					
+				}
+			})
+		},
+		baselist : function (opts, template, build) {
+			return UMG.base(build.type,opts,{
+				$link : function (list, scope, attrs) {
+					var elem = list					
+					var old_update = list.update.bind(list)
+					var old_sync = list.sync.bind(list)
+					var old_children = list.enumerate_children.bind(list)
+					function filter_out() {
+						old_children(function (row) {
+							if (!JavascriptWidget.HasValidCachedWidget(row)) {
+								list.remove_child(row,true)
+							}
+						})						
+					}					
+					
+					list.filter_out = filter_out
+															
+					list.enumerate_children = function (fn) {
+						filter_out()
+						old_children(fn);						
+					}
+					
+					list.update = function () {
+						filter_out()
+						old_update()												
+					}
+					list.sync = function () {
+						filter_out()
+						old_sync()	
+					}
+					
+					
+					var repeat = attrs.Repeat || {}
+					var item_key = 'item';
+					build.register( list, scope, attrs, function (obj) {
+						// for scope inheritance
+						function ChildScopeClass() {}
+						ChildScopeClass.prototype = scope
+						
+						var child_scope = new ChildScopeClass()
+						child_scope.$id = scope_id++						
+						child_scope[item_key] = obj.json;				
+						
+						obj.$scope = child_scope		
+						
+						var row = elem.add_child(template,child_scope,true);						
+						child_scope.$apply = function () {
+							if (child_scope.$guard) {
+								row.update()
+								row.sync()								
+							}
+							else {
+								child_scope.$guard = true
+								scope.$apply()							
+								child_scope.$guard = false
+								row.update()
+								row.sync()
+							}							
+						}
+						return row;
+					})
+					
+					var last_items = []
+					function alloc(k) {
+						return new JavascriptObject()
+					}
+					function set_data(json,skip) {
+						var items = []
+						if (_.isArray(json)) {
+							json.forEach(function(v,k){
+								if (v.key != undefined) {
+									k = v.key									
+								}								
+								var old = _.find(last_items, function (x) {								
+									return x.json.key === k
+								})
+								var obj = old || alloc(k)
+								obj.json = v			
+								items.push(obj)                     
+							})							
+						}						
+						
+						last_items = items
+						list.Items = _.sortBy(items, 'json.key')
+						if (!skip && JavascriptWidget.HasValidCachedWidget(list)) list.RequestListRefresh()
+					}
+					
+					var arr = repeat.split(' in ');
+					if (arr.length != 2) {							
+						throw "repeat requires x in y";
+					}				
+					item_key = arr[0];
+					var fn = instantiator.interpolate('generator',arr[1]);
+					set_data(fn.call(scope,true));
+					
+					list.add_binding(function(instance,scope_instance) {							
+						set_data(fn.call(scope_instance));
+					});
+				} 
+			})			
+		},
+		list : function (opts, template) {
+			if (_.isString(template)) template = UMG.text({},template) 
+			return this.baselist(opts, template, {
+				type : JavascriptListView,
+				register : function (elem, scope, attrs, obj) {
+					elem.JavascriptContext = Context
+					
+					var onClick = instantiator.interpolate('handler',attrs.OnClick);
+					var onDoubleClick = instantiator.interpolate('handler',attrs.OnDoubleClick);					 										
+					
+					elem.proxy = {
+						OnClick : function (json) {
+							onClick && onClick.call(json.$scope)
+							json.$scope.$apply()
+						}, 
+						OnDoubleClick : function (json) {
+							onDoubleClick && onDoubleClick.call(json.$scope)
+							json.$scope.$apply()
+						},
+						OnSelectionChanged : function (json,mode) {
+							//console.log('changed', json, mode)
+						}
+					} 
+					elem.OnGenerateRowEvent.Add(obj)
+				}
+			})						
+		},
+		tile : function (opts, template) {
+			if (_.isString(template)) template = UMG.text({},template) 
+			return this.baselist(opts, template, {
+				type : JavascriptTileView,
+				register : function (elem, scope, attrs, obj) {
+					elem.JavascriptContext = Context
+					
+					var onClick = instantiator.interpolate('handler',attrs.OnClick);
+					var onDoubleClick = instantiator.interpolate('handler',attrs.OnDoubleClick);					 										
+					
+					elem.proxy = {
+						OnClick : function (json) {
+							onClick && onClick.call(json.$scope)
+							json.$scope.$apply()
+						}, 
+						OnDoubleClick : function (json) {
+							onDoubleClick && onDoubleClick.call(json.$scope)
+							json.$scope.$apply()
+						},
+						OnSelectionChanged : function (json,mode) {
+							//console.log('changed', json, mode)
+						}
+					}
+					
+					elem.OnGenerateTileEvent.Add(obj)
+				}
+			})						
+		},		
+		PropertyEditor : function (opts) {
+			return UMG.base(PropertyEditor,opts,{            
+				$link : function (edit,scope,attrs) {
+					function set_data(x) {
+					edit.SetObject(x) 
+					}
+					var fn = instantiator.interpolate('generator',attrs.Target);
+					set_data(fn.call(scope,true));
+					
+					edit.add_binding(function(instance,scope_instance) {							
+						set_data(fn.call(scope_instance));
+					});                
+					edit.OnChange.Add( function (param) {                    
+						if (update != null) {
+							update()
+						}
+					})                    
+				}
+			})
+		}
+	}
+	
+	function Style(id,attrs) {
+		var s = split(id)
+        var doc = {
+			class : s.class,
+			type : s.type,
+            attrs: _.clone(conv(attrs)) || {id:s.id},
+            children: Array.prototype.slice.call(arguments,2)
+        };
+		
+        
+        return doc
+	}
+
+	var maker = require('editor-maker')	
+
+	UMG.TabManager = function (opts, children) {
+	    if (!_.isArray(children)) {
+	        children = Array.prototype.slice.call(arguments, 1);
+	    }
+	    return UMG.base(JavascriptEditorTabManager, opts, {
+	        $link: function (manager, scope) {
+	            var ids = []
+	            var next_id = 0
+
+                var instances = []
+				
+				// @HACK : ?�건 style???�로 ?�긴 control???�???�용?��? ?�는 문제 ?�문??발생?�는 것인??..
+				// ?�실 ??control??extended style??갖고 ?�??직접 ?�용??주면 ??�?같습?�다.
+				var pending_fn = null
+
+	            var Tabs = children.map(function (child) {
+	                var id = child.attrs.Id || 'inner' + next_id;
+	                next_id++
+	                var tab = maker.tab({ TabId: id }, function (context) {
+	                    var instance = instantiator(child, scope)
+						if (pending_fn) {
+							pending_fn(instance)
+						}
+	                    instances.push(instance)
+                        return instance
+	                })
+	                ids.push(id);
+	                return tab
+	            })
+
+	            function filter_out() {
+	                instances = _.filter(instances, function (tab) {
+	                    return JavascriptWidget.HasValidCachedWidget(tab);
+	                });
+	            }
+
+	            var old_children = manager.enumerate_children.bind(manager)				
+	            manager.enumerate_children = function (fn) {	                
+	                old_children(fn);
+	                filter_out()
+					console.log('enum! children!',instances.length)
+					pending_fn = fn
+                    instances.forEach(function (instance) {
+                        fn(instance)
+                    });
+	            }
+
+	            var old_update = manager.update.bind(manager)
+	            var old_sync = manager.sync.bind(manager)
+
+	            manager.update = function () {
+	                filter_out()
+	                old_update()
+	                instances.forEach(function (instance) {
+	                    instance.update();
+	                });
+	            }
+	            manager.sync = function () {
+	                filter_out()
+	                instances.forEach(function (instance) {
+	                    instance.sync();
+	                });
+	            }
+
+	            var layout = {
+	                Name: "jseditor_layoutv18",
+	                PrimaryAreaIndex: 0,
+	                Areas: [
+                        {
+                            Type: 'Area',
+                            SizeCoefficient: 1,
+                            WindowPlacement: 'Placement_NoWindow',
+                            Orientation: 'Orient_Horizontal',
+                            nodes: ids.map(function (id) {
+                                return {
+                                    Type: 'Stack',
+                                    SizeCoefficient: 1 / ids.length,
+                                    HideTabWell: true,
+                                    Tabs: [
+                                        { TabId: id, TabState: 'OpenedTab' }
+                                    ]
+                                }
+                            })
+                        }
+	                ]
+	            }
+
+	            manager.Tabs = Tabs
+	            manager.Layout = JSON.stringify(layout)
+	        }
+	    })
+	}
+	
+	var NextContextId = 0
+	global.CommandCache = global.CommandCache || {}
+	UMG.CommandBuilder = function (Id, opts, children) {
+	    if (!_.isArray(children)) {
+	        children = Array.prototype.slice.call(arguments, 2)
+	    }
+	    var ContextId = "UMGCommandBuilder_" + Id
+
+	    return function (elem) {	        
+	        if (global.CommandCache[ContextId]) {
+	            global.CommandCache[ContextId].Discard()
+	        }
+	        var obj = {}
+			var scope = null
+	        children.forEach(function (child, key) {
+	            var id = child.id || key
+	            obj[id] = _.extend({scope:function() { return scope }},child)				
+	        })
+	        var commands = global.CommandCache[ContextId] = maker.commands({
+	            name: ContextId,
+	            commands: obj
+	        })
+
+	        commands.Commit()
+
+	        elem.Bind(commands)
+
+	        children.forEach(function (child, key) {
+	            var id = child.id || key
+	            elem.AddMenuEntry(commands, id)
+	        })
+			
+			return function (_scope) {
+				scope = _scope;
+			}
+	    }
+	}	
+	
+	UMG.MenuBar = function (opts, children) {
+		children = Array.prototype.slice.call(arguments, 1)
+		var menu = {}
+		children.forEach(function (child) {
+			menu[child.Id] = child
+		})
+		var postfix = []
+		var bound_scope = null
+		var opts = {
+			OnHook: function (type) {				
+				if (type == 'Menubar') {
+					for (var id in menu) {
+						var child = menu[id];
+						var displayName = child.DisplayName || '<MENU>';
+						var tooltip = child.Tooltip || '';
+						JavascriptEditorMenu.AddPullDownMenu(id, displayName, tooltip)
+					}
+				}
+				else {					
+					var child = menu[type]
+					if (_.isFunction(child.$link)) {
+						var fn = child.$link(JavascriptUIExtender)						
+						if (_.isFunction(fn)) {
+							postfix.push(fn)
+							if (bound_scope) {
+								fn(bound_scope)
+							}
+						}
+					}					
+				}
+				UMG.$$temp = null
+			}			
+		}
+		return UMG.base(JavascriptEditorMenu, opts,{
+			$link: function (elem, scope, attrs) {
+				bound_scope = scope
+				postfix.forEach(function(fn){
+					fn(scope)
+				})
+			}
+		})      
+	};
+
+	UMG.Menu = function (opts, commands) {
+	    commands = Array.prototype.slice.call(arguments, 1)
+	    var args = [opts.Id, {}]
+	    args = args.concat(commands)
+	    opts.$link = opts.$link || UMG.CommandBuilder.apply(UMG, args)
+	    return opts
+	}
+	
+	UMG.MenuEntry = function (opts) {
+		opts = conv(opts)
+		var bindings = instantiator.make_fnbinding(opts.Fn || {});
+		var _bindings = instantiator.make_binding(opts.Binding || {});
+		var holder = null
+		var obj = {
+			OnExecute : {
+				Add : function (fn) {
+					holder.execute = fn
+				},
+				Clear : function () {}
+			},
+			OnQuery : {
+				Add : function (fn) {
+					holder.query = fn
+				},
+				Clear : function () {}
+			}
+		}		 
+	    return _.extend({
+	        Query: function (what) {
+				var test = {}
+				var answer = undefined
+				_bindings(test,this.scope());
+				
+				if (what == 'checked') {
+					answer = test.Checked
+				} else if (what == 'visible') {
+					answer = test.Visible
+				} else if (what == 'enabled') {
+					answer = test.Enabled
+				}
+				
+				return answer == undefined ? true : answer;
+			},
+			Execute: function () {
+				if (holder == null) {
+					holder = {}
+					bindings(obj,this.scope());
+				} 
+				holder.execute && holder.execute()
+			}	        
+	    }, opts)
+	}
+	UMG.MenuButton = function (opts) {
+	    return UMG.MenuEntry(_.extend({ type: 'Button' }, opts))
+	}
+	UMG.MenuToggleButton = function (opts) {
+	    return UMG.MenuEntry(_.extend({ type: 'ToggleButton' }, opts))
+	}
+	
+	module.exports = _.extend(UMG.generic,UMG);
+}(this))
diff --git a/Plugins/UnrealJS/Content/Scripts/assert.js b/Plugins/UnrealJS/Content/Scripts/assert.js
new file mode 100644
index 0000000..77de46d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/assert.js
@@ -0,0 +1,3 @@
+module.exports = function () {
+    console.assert.apply(console,arguments)
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/css-split.js b/Plugins/UnrealJS/Content/Scripts/css-split.js
new file mode 100644
index 0000000..310b45e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/css-split.js
@@ -0,0 +1,22 @@
+(function () {
+	'use strict';
+	function split(id) {
+		var doc = {			
+		}
+		id.replace(/(^[^#\.]+|[#\.][^#\.]+)/g,function(match){
+            if (match[0] == '#') {
+                doc.id = match.substr(1)
+            } else if (match[0] == '.') {                
+                doc.class = doc.class || []
+                doc.class.push(match.substr(1)) 
+            } else {
+                doc.type = eval(match)
+				if (doc.type == undefined) {
+					throw "Undefined type " + match + " for instantiator"
+				}
+            }
+        })
+		return doc
+	}
+	module.exports = split;
+}())
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/devrequire.js b/Plugins/UnrealJS/Content/Scripts/devrequire.js
new file mode 100644
index 0000000..7b96e58
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/devrequire.js
@@ -0,0 +1,94 @@
+var _ = require('lodash')
+
+// Live reload 구현
+var devrequire = function (opts) {
+    var target
+    if (_.isString(opts)) {
+        target = opts
+        opts = {}
+    }
+    opts = opts || {};
+    var self = this;
+    function default_get_change(watcher) {
+        var has_changed = function (x) { return watcher.Contains(x) }
+        var changed_modules = _.filter(_.values(modules), has_changed)
+        return changed_modules        
+    }    
+    function default_exec() {
+        self.purge_modules()        
+        return require(target)()
+    }
+    var get_change = opts.get_change || default_get_change;
+    var exec = opts.exec || default_exec;
+    var should_notify = opts.notify || (self.JavascriptNotification != undefined);
+    var notification_message = opts.message || "Hot reload(JS)"       
+    
+    var cleanup = exec()
+    if (!_.isFunction(cleanup)) {
+        cleanup = function () { }
+    }
+    
+    /** aggregated watcher */
+    var watcher = {
+        list : [],
+        OnChanged : {
+            list : [],
+            Add : function (fn) {
+                this.list.push(fn)
+            }
+        },
+        Watch : function (path) {
+            var w = new DirectoryWatcher()
+            var self = this;
+            w.OnChanged.Add(function () {
+                self.OnChanged.list.forEach(function (fn) { fn(); });
+            });
+            w.Watch(path);
+            this.list.push(w)
+        },
+        Discard : function () {
+            this.list.forEach(function (w) {
+                w.Unwatch()
+            })
+        },
+        Contains : function (x) {
+            return _.any(this.list,function (w) {
+                return w.Contains(x);
+            });
+        }
+    };
+    
+    watcher.OnChanged.Add(function () {
+        var changed_modules = get_change(watcher)
+        var module_changed = changed_modules.length > 0
+        if (module_changed) {
+            cleanup()            
+            cleanup = exec()
+            gc()
+            if (!_.isFunction(cleanup)) {
+                cleanup = function () { }
+            }
+
+            var file = _.unique(changed_modules).join(',')
+
+            if (should_notify) {
+                var note = new JavascriptNotification
+                note.Text = notification_message + ": " + file
+                note.bFireAndForget = true
+                note.Fire()
+                note.ExpireDuration = 3
+                note.Success()
+            }
+        }
+    })
+    
+    Context.Paths.forEach(function(dir){
+        watcher.Watch(dir)
+    });    
+    
+    return function () {
+        watcher.Discard()
+    }
+}
+
+module.exports = devrequire
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/editor-maker.js b/Plugins/UnrealJS/Content/Scripts/editor-maker.js
new file mode 100644
index 0000000..bc40e8c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/editor-maker.js
@@ -0,0 +1,109 @@
+function box_extension(where) {
+	return function (opts) {
+		var menux = new JavascriptUIExtender	
+		var builders = {}
+		var exts = []
+		for (var k in opts) {
+			var v = opts[k]
+			var position = v.position || 'After'			
+			exts.push({ExtensionHook : k, HookPosition : position})
+
+			var fn = v.builder
+			builders[k] = fn
+		}
+
+		menux[where] = exts
+		menux.OnHook.Add( function (hook) {
+			var fn = builders[hook]
+			if (fn) {
+				fn(menux)
+			}			
+		})
+		return menux
+	}		
+}	
+
+
+module.exports = {
+	menu : box_extension('MenuExtensions'),
+	toolbar : box_extension('ToolbarExtensions'),
+	tab : function MakeTab(opts,tab_fn) {
+		opts = opts || {}
+			
+		var tab = new JavascriptEditorTab
+		tab.TabId = opts.TabId || 'TestJSTab'
+		tab.Role = opts.Role || 'NomadTab'        
+		tab.DisplayName = opts.DisplayName || '안녕하세요!'
+		tab.OnSpawnTab.Add(tab_fn)
+		return tab
+	},
+	commands : function make_commands(opts) {
+		var commands = new JavascriptUICommands
+
+		opts = opts || {}
+		commands.ContextName = opts.name || opts.Name || ''
+		commands.ContextDesc = opts.description || opts.Description || 'context description'
+		commands.ContextNameParent = opts.parent || opts.Parent || ''
+		commands.StyleSetName = opts.styleset || opts.Styleset || 'None'
+
+		var org_cmds = opts.commands || {}
+
+		var cmds = []
+		for (var k in org_cmds) {
+			var v = org_cmds[k]
+			cmds.push({
+				Id : k,
+				FriendlyName : v.name || v.Name,
+				Description : v.description || v.Description,
+				ActionType : v.type || v.Type || 'Button'
+			})
+		}
+		commands.Commands = cmds	
+		commands.OnExecuteAction.Add( function (action) {
+			var fn = org_cmds[action]
+			fn && fn.execute && fn.execute()
+            fn && fn.Execute && fn.Execute()
+		})		
+
+		"OnCanExecuteAction/enabled OnIsActionChecked/checked OnIsActionButtonVisible/visible".split(' ')
+		.forEach( function (v) {
+			var xy = v.split('/')
+			var x = xy[0]
+			var y = xy[1]
+			commands[x].Add( function (action) { 
+			    var fn = org_cmds[action]
+			    if (fn && fn.query) {
+			        return fn.query(y)
+			    }
+			    if (fn && fn.Query) {
+			        return fn.Query(y)
+			    }
+				return true
+			})
+		})
+		return commands
+	},
+	editor : function make_editor(opts) {
+		var editor = new JavascriptAssetEditorToolkit
+		editor.ToolkitFName = 'jseditor'
+		editor.BaseToolkitFName = 'jseditor_base'
+		editor.ToolkitName = 'jseditor toolkit'
+		editor.WorldCentricTabPrefix = 'jseditor'		
+		
+		editor.Layout = JSON.stringify(opts.layout)
+				
+		if (opts.tabs != undefined) {
+			editor.Tabs = opts.tabs			
+		}
+		if (opts.commands != undefined) {
+			editor.Commands = opts.commands
+		}
+		if (opts.menu != undefined) {
+			editor.MenuExtender = opts.menu
+		}		
+		if (opts.toolbar != undefined) {
+			editor.ToolbarExtender = opts.toolbar
+		}		
+		return editor
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/fs.js b/Plugins/UnrealJS/Content/Scripts/fs.js
new file mode 100644
index 0000000..13a3db7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/fs.js
@@ -0,0 +1,11 @@
+module.exports = {
+    readFileSync : function (path,encoding) {
+        var text = Context.ReadScriptFile(path);
+        if (encoding == 'utf8') return text;
+        return {
+            toString: function () {
+                return text;
+            }
+        }
+    }
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/input-binding.js b/Plugins/UnrealJS/Content/Scripts/input-binding.js
new file mode 100644
index 0000000..216749d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/input-binding.js
@@ -0,0 +1,20 @@
+(function () {
+    "use strict"
+
+    module.exports = function AddBinding(PC_C,binding) {
+        let theClass = eval(`Input${binding.type}DelegateBinding`)
+        let bindingObject = null
+        if (!theClass) throw "Invalid binding type"
+		
+        bindingObject = JavascriptLibrary.GetDynamicBinding(PC_C,theClass)
+        if (!bindingObject) {
+            bindingObject = new theClass()
+            JavascriptLibrary.AddDynamicBinding(PC_C,bindingObject)
+        }		
+		
+        let key = `Input${binding.type}DelegateBindings`
+        let prev = bindingObject[key]
+        prev.push(binding)
+        bindingObject[key] = prev
+    }
+})()
diff --git a/Plugins/UnrealJS/Content/Scripts/instantiator.js b/Plugins/UnrealJS/Content/Scripts/instantiator.js
new file mode 100644
index 0000000..2b79c68
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/instantiator.js
@@ -0,0 +1,470 @@
+(function () {
+  "use strict";
+  
+  var _ = require('lodash');
+  
+  function Tokenizer(cb) {
+    var rules = [];
+    var buf = '';
+    
+    function test(buf) {
+      for (var kk in rules) {
+        var rule = rules[kk];
+        if (rule[0].test(buf)) return rule;
+      }
+    }
+    
+    var prev_rule = null;
+    return {
+      addRule : function (regex, token) {
+        rules.push([regex,token]);
+      },
+      write : function (str) {
+        for (var k in str) {
+          var ch = str[k];
+          var rule = prev_rule;
+          if (rule) {
+            if (rule[0].test(buf+ch)) {
+              buf += ch;
+            } else {
+              cb(buf,{type:prev_rule[1]});
+              buf = ch;
+              prev_rule = test(buf);
+            }
+          } else {
+            buf += ch;
+            prev_rule = test(buf);
+          }
+        }
+      },
+      end : function () {
+        var rule = test(buf);
+        if (rule) cb(buf,{type:rule[1]});
+        prev_rule = null;
+        buf = '';
+      }
+    }
+  }
+  
+  function tk(cb) {
+    var t = Tokenizer(cb);
+    
+    t.addRule(/^"([^"\n]|\\")*"?$/, 'quote');
+    t.addRule(/^'([^'\n]|\\')*'?$/, 'quote');
+    t.addRule(/^'[^']*$/, 'char continue');
+    t.addRule(/^\($/, 'L-paren');
+    t.addRule(/^\)$/, 'R-paren');
+    t.addRule(/^\[$/, 'L-bracket');
+    t.addRule(/^\]$/, 'R-bracket');
+    t.addRule(/^\.$/, 'dot');
+    t.addRule(/^\|$/, 'pipe');
+    t.addRule(/^\^$/, 'carrot');
+    t.addRule(/^;$/, 'semicolon');
+    t.addRule(/^([-<>~!%^*\/+=?|.,:;]|\|\||&&|--|\+\+|[-+*|%\/=]=)$/, 'opeartor');
+    t.addRule(/^([_A-Za-z\$]\w*)$/, 'identifier');
+    t.addRule(/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/, 'number');
+    t.addRule(/^(\s+)$/, 'whitespace');
+    
+    return t;
+  }
+  
+  var out = '';
+  var sentences = []
+  var last = undefined
+  var lexer = tk(function(src,token){    
+    if (last != 'carrot' && token.type == 'identifier' && last != 'dot') {       
+      out += 'this.';      
+    }
+    if (last == 'carrot' && token.type != 'identifier') {
+      out += '^';
+    } 
+    if (token.type != 'carrot') {
+      out += src;
+    }
+    last = token.type;
+    if (token.type == 'semicolon') {
+      sentences.push(out);
+      out = '';
+    }
+  });  
+  
+  function wrap(source) {
+    out = '';
+    sentences.length = 0;
+    lexer.write(source);
+    lexer.end();
+    sentences.push('return ' + out);
+    return sentences.join(' ');
+  }
+  
+  function make_fn(name,str) {
+     var source = "function " + name + "() { try { " + str + " } catch (e) { return '' } }; " + name + ";";
+    return eval(source);
+  }
+  
+  function beautify(k) {
+    return k.split('-').map(function(x){
+      return x.charAt(0).toUpperCase() + x.slice(1);
+    }).join('');
+  }
+  
+  function conv(obj) {
+    var out = {};
+    for (var k in obj) {
+      var v = obj[k];
+      var index = k.indexOf('.');
+      if (index>=0) {
+        var lhs = k.substr(0,index);
+        var rhs = k.substr(index+1);
+        var tmp = {}
+        tmp[rhs] = v;
+        k = lhs;
+        v = tmp;        
+      }
+      k = beautify(k);
+      if (_.isObject(v) && !_.isFunction(v) && _.keys(v).length > 0) {
+        out[k] = out[k] || {};
+        _.extend(out[k],conv(v));
+      } else {
+        out[k] = v;
+      }
+    }
+    return out;
+  }
+  
+  function set_attr(instance,k,attr) {
+    function inner(k) {
+      var setter = instance["Set"+k]    
+      if (setter != undefined) { 
+        setter.call(instance,attr)
+        return true
+      } else if (instance[k] != undefined) {         
+        instance[k] = attr;              
+        return true
+      } else {
+        return false
+      }      
+    }
+    
+    inner(k) || inner("b"+k)    
+  }
+  
+  function interpolate(k,v) {
+    return make_fn(k,wrap(v));
+  }
+  
+  function make_binding(attrs) {
+    var out = {}
+    var funcs = []
+    function add(dest,k,fn) {      
+      funcs.push( function (scope) {
+        dest[k] = fn.call(scope);
+      })
+    }
+    function visit(o,dest) {
+      for (var k in o) {
+        var v = o[k]        
+        if (_.isString(v)) {
+          var fn = interpolate(k,v);          
+          add(dest,k,fn);          
+        } else if (_.isObject(v)) {
+          out[k] = {}
+          visit(o[k],dest[k])
+        }
+      }
+    }    
+    
+    visit(attrs,out)    
+    
+    return function (instance,scope) {
+      funcs.forEach(function (f) {f(scope)});
+      set_attrs(instance,out);
+    }
+  }
+  
+  function make_fnbinding(attrs) {
+    var out = {}
+    var funcs = []
+    var has_init = false
+    function add(dest,k,fn) {      
+      funcs.push( function (scope) {
+        var F = function() {
+          var ret = fn.apply(scope,arguments); 
+          if (!F.no_apply) {
+            scope.$apply();            
+          }
+          return ret;           
+        };
+        dest[k] = F;
+      })
+    }
+    
+    function visit(o,dest) {
+      for (var k in o) {
+        var v = o[k]
+        if (_.isString(v)) {
+          var fn = interpolate(k,v);          
+          add(dest,k,fn);          
+        }
+      }
+    }    
+    
+    visit(attrs,out)    
+    
+    return function (instance,scope) {  
+      funcs.forEach(function (f) {f(scope)});
+      if (out.Init) {
+        if (!has_init) {
+          out.no_apply = true
+          out.Init()
+          out.no_apply = false
+          has_init = true          
+        } else {
+          delete out.Init
+        }        
+      }
+      set_attrs(instance,out);
+    }
+  }
+  
+  function set_attrs(instance,attrs,scope) {
+    var bindings = []
+    for (var k in attrs) {
+      if (k == 'Binding') {         
+        if (_.isObject(scope)) {          
+          var v = attrs[k];    
+          var fn = make_binding(v);               
+          bindings.push(fn);
+        } else {
+          console.error('Binding requires scope')
+        }        
+      } else if (k == 'Fn') { 
+        if (_.isObject(scope)) {
+          var v = attrs[k];
+          var fn = make_fnbinding(v);               
+          bindings.push(fn);
+        } else {
+          console.error('Binding requires scope')
+        }        
+      } else {              
+        bindings.concat(set_attr(instance,k,attrs[k]));
+      }      
+    }
+    return bindings;
+  }
+  
+  var next_id = 0
+
+  function instantiate(design,scope) {
+    if (!design || !design.type) {
+      throw 'failed to instantiate no design!'
+    }
+    
+    scope = scope || function () { return {} }
+    var bindings = [];
+    var custom_bindings = [];
+    var instance = new design.type();
+    if (!instance) {
+       throw 'failed to instantiate' + design.type
+    }
+    
+    function update() {
+      var scope_instance = scope
+      function kick(fn) {
+        fn(instance,scope_instance);
+      };
+      bindings.forEach(kick);      
+      custom_bindings.forEach(kick);
+      instance.children.forEach(function (child) { 
+        child.instance.update();
+        if (child.bindings) {
+          child.bindings.forEach( function (fn) {
+            fn(child.slot,scope_instance);
+          });
+        }         
+      });
+    };
+    
+    instance.id = next_id++
+    instance.class = design.class || []
+    
+    instance.add_binding = function (binding) {
+      custom_bindings.push(binding);
+    };
+       
+    instance.sync = function () {
+      JavascriptWidget.CallSynchronizeProperties(instance)
+      instance.children.forEach(function (child) {
+        child.instance.sync();
+        JavascriptWidget.CallSynchronizeProperties(child.slot);
+      });
+    };
+    
+    instance.type = design.type;
+    instance.set_attrs = function (attrs) {
+      set_attrs(instance,attrs,scope);
+    }
+    
+    instance.update = update;
+    instance.find = function (id) {
+      if (design.id == id) return instance
+
+      var o = instance.ids[id];
+      if (o != undefined) return o;
+
+      for (var k in instance.children) {
+        var o = instance.children[k].instance.find(id);
+        if (o != undefined) return o;
+      }    
+    }
+    instance.ids = {}
+    instance.children = []
+    
+    var attrs = design.attrs || {}   
+
+    bindings = set_attrs(instance,attrs,scope)
+    
+    var children = design.children || []
+    
+    function add_child(child,scope,no_directop) {
+      var c = instantiate(child,scope);
+      var slot, child_bindings;      
+      if (!no_directop) {        
+        slot = instance.AddChild(c);
+        if (slot) {
+          child_bindings = set_attrs(slot,child.slot || {},scope);
+          c.set_slot_attrs = function (attrs) {
+            set_attrs(slot,attrs)
+          }          
+        } else {
+          console.error("No slot to add")
+        }  
+      }
+
+      if (child.id != undefined) {
+        instance.ids[child.id] = c;
+      }
+      
+      if (instance.cached_extended_styles) {        
+        c.set_styles(instance.cached_extended_styles);
+      }
+
+      instance.children.push({instance:c,slot:slot,bindings:child_bindings,template:child});
+      return c;      
+    }
+    
+    function remove_child(child_instance,no_directop) {
+      var child = _.find(instance.children,function (child) {         
+        return child.instance == child_instance; 
+      });
+      if (child) {
+        if (child.id != undefined) {
+          delete instance.ids[child.id]
+        }
+        instance.children.splice(instance.children.indexOf(child),1)
+      } else {
+        console.error('couldnot find child')
+      }
+      if (!no_directop) {
+       child_instance.RemoveFromParent(); 
+      }      
+    }
+    
+    instance.add_child = add_child;
+    instance.remove_child = remove_child;
+    
+    children.forEach( function (child) {
+      add_child(child,scope);
+    });
+    
+    function update_styles(styles) {            
+      var matched_styles = _.filter(styles,function (style) {
+        return (!style.type || style.type == design.type) && (!style.id || style.id == design.id) && (!style.class || _.all(style.class || [],function (k) { return _.contains(design.class || [],k)}));
+      }).map(function(style){
+        style = _.clone(style);        
+        if (style.weight == undefined) {
+          var base = 0;
+          if (style.id) { base = 3; }
+          else if (style.class) { base = 2; }
+          style.weight = base;          
+        }        
+        return style;
+      });
+      
+      var computed = {}
+      var has_computed = false
+      var extended = _.filter(styles,function (style) {
+        return !style.hard
+      });       
+      
+      function deepExtend(out,src) {
+        for (var k in src) {
+          var v = src[k]
+          if (out[k] == undefined) {
+            out[k] = v
+          } else {
+            if (_.isObject(v)) {
+              if (out[k]) {
+                deepExtend(out[k],v)
+              }
+            } else {
+              out[k] = v
+            }
+          }
+        }
+      }
+      
+      _.sortBy(matched_styles,'weight').forEach( function(style) {
+        // console.log(style.weight,JSON.stringify(style))
+        deepExtend(computed,style.attrs)       
+        if (style.children) {          
+          extended = extended.concat(style.children.map(function(child_style){
+            child_style = _.clone(child_style)
+            child_style.weight = style.weight + 10
+            return child_style
+          }))
+        }        
+        has_computed = true
+      })      
+      
+      if (has_computed) {
+        var slot = computed.Slot;        
+        instance.set_attrs(computed);        
+        if (slot && instance.set_slot_attrs) {          
+          instance.set_slot_attrs(slot);          
+        }        
+      }
+      
+      instance.cached_extended_styles = extended
+      
+      instance.enumerate_children(function (child) {
+          child.set_styles(extended);
+      })
+    }
+    
+    instance.enumerate_children = function (fn) {
+      if (_.isArray(instance.children)) {
+        instance.children.forEach(function (child) {
+          fn(child.instance);
+        });
+      }      
+    }
+    
+    instance.set_styles = update_styles;					
+    
+    if (design.$link != undefined) {
+      design.$link(instance,scope,attrs || {});
+    }
+    
+    update();
+    
+    return instance;
+  }
+  
+  instantiate.interpolate = interpolate;
+  instantiate.conv = conv;
+  instantiate.make_fnbinding = make_fnbinding;
+  instantiate.make_binding = make_binding;
+  
+  module.exports = instantiate;  
+}());
diff --git a/Plugins/UnrealJS/Content/Scripts/jade-umg.js b/Plugins/UnrealJS/Content/Scripts/jade-umg.js
new file mode 100644
index 0000000..beb7002
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/jade-umg.js
@@ -0,0 +1,152 @@
+var _ = require('lodash');
+var fs = require('fs');
+var lex = require('jade-lexer');
+var stripComments = require('jade-strip-comments');
+var parse = require('jade-parser');
+var load = require('jade-load');
+//var filters = require('jade-filters');
+var link = require('jade-linker');
+
+
+module.exports = function (UMG,file) {	
+	var path = {
+		join : function () {			
+			return Array.prototype.join.call(arguments,'/')
+		},
+		dirname : function (x) {
+			var arr = x.split('/')
+			arr.length = arr.length - 1
+			return arr.join('/')
+		},
+		basename : function (x) {
+			var arr = x.split('/')
+			var y = arr[arr.length-1].split('.')
+			if (y.length>1) {
+				y.length = y.length - 1
+			}			
+			return y.join('.')
+		}
+	}
+	function test(str,options) {
+		var debug_sources = {};
+		debug_sources[options.filename] = str;
+		var dependencies = [];
+		try {
+			var ast = load.string(str, options.filename, {
+			lex: lex,
+			parse: function (tokens, filename) {          
+				tokens = stripComments(tokens, { filename: filename });        
+				return parse(tokens, filename);
+			},
+			resolve: function (filename, source) {
+				filename = filename.trim();
+				if (filename[0] !== '/' && !source)
+				throw new Error('the "filename" option is required to use includes and extends with "relative" paths');
+		
+				if (filename[0] === '/' && !options.basedir)
+				throw new Error('the "basedir" option is required to use includes and extends with "absolute" paths');
+					
+				filename = path.join(filename[0] === '/' ? options.basedir : path.dirname(source), filename);
+		
+				if (path.basename(filename).indexOf('.') === -1) filename += '.jade';
+		
+				return filename;
+			},
+			read: function (filename) {
+				dependencies.push(filename);
+				var str = fs.readFileSync(filename, 'utf8');
+				debug_sources[filename] = str;
+				return str;
+			}
+			});
+			ast = link(ast);
+			//ast = filters.handleFilters(ast, exports.filters);
+			return ast
+		} catch (err) {    
+			console.log("ERROR!",String(err))
+			if (err.code && typeof err.code === 'string' && err.code.substr(0, 4) === 'JADE') {
+			runtime.rethrow(
+				new Error(err.msg),
+				err.filename,
+				err.line,
+				options.filename === err.filename ? str : (options.compileDebug ? debug_sources[err.filename] : undefined)
+			);
+			}
+			throw err;
+		}
+	}
+	
+	var filenames = []
+    
+    function umg_jade(ast) {		
+		
+        function text(ast) {
+            if (ast.val.trim().length == 0) return;
+                        
+            return UMG.text({},ast.val)
+        }
+        function tag(ast) {             
+            var attrs = {}
+            ast.attrs.forEach(function(attr){
+				var val                
+                if (attr.escaped) {                    
+                    val = eval(attr.val);                    
+                } else {                    
+                    val = eval(attr.val); 
+                }      
+				if (attrs[attr.name]) {
+					if (_.isArray(attrs[attr.name])) {
+						attrs[attr.name].push(val)
+					} else {
+						attrs[attr.name] = [attrs[attr.name],val]
+					}
+				} else {
+					attrs[attr.name] = val
+				}      
+            })
+            var children = [];
+            if (ast.name != 'text') {
+                children = block(ast.block);
+            } else {
+                children = [ast.block.nodes[0].val];
+            }
+            children = _.compact(children)
+            var args;
+            if (UMG[ast.name]) {
+                args = [attrs];
+                args = args.concat(children);
+                
+                return UMG[ast.name].apply(UMG,args)
+            } else{
+                args = [ast.name,attrs];
+                args = args.concat(children);
+                return UMG.apply(UMG,args);
+            }
+        }
+        function block(ast) {
+			if (ast.filename) {				
+				filenames.push(ast.filename)
+			}						
+            return _.compact(ast.nodes.map(function (node) {
+                if (node.type == 'Tag') {
+                    return tag(node);
+                } 
+                else if (node.type == 'Text')
+                {
+                    return text(node)
+                } 
+				else if (node.type == 'Block')
+				{
+					return block(node)[0]
+				}
+            }))
+        }       
+		
+		return block(ast);
+    }    
+    
+	var ast = test(fs.readFileSync(file,'utf8'),{filename:file})
+    var design = umg_jade(ast)[0];	
+	design.$files = _.unique(filenames)
+	return design;
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/jsconfig.json b/Plugins/UnrealJS/Content/Scripts/jsconfig.json
new file mode 100644
index 0000000..8c4e447
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/jsconfig.json
@@ -0,0 +1,5 @@
+{	
+	"compilerOptions":{
+		"target": "ES6"
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/liveapp.js b/Plugins/UnrealJS/Content/Scripts/liveapp.js
new file mode 100644
index 0000000..cefb59e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/liveapp.js
@@ -0,0 +1,61 @@
+(function () {
+	"use strict"
+
+	function liveapp(opts) {
+		opts = opts || {}
+		var view = opts.view
+		var root_scope = opts.scope || function () {}
+		var fixup = opts.$link || function () {}	
+		
+		var UMG = require('UMG')	
+		
+		function main() {
+			var page = new VerticalBox()
+			
+			function test(path) {
+				var design = require('jade-umg')(UMG,path)						
+				var app = UMG.app(design,root_scope);
+				
+				fixup(app)
+				
+				var slot = page.AddChild(app)		
+				slot.Size = {SizeRule:'Fill',Value:1}
+				var cleanup = function () {
+					app.RemoveFromParent()
+				}
+				cleanup.$files = design.$files
+				return cleanup
+			} 
+			
+			function livereload(path,test) {
+				var files = [path]        
+				var fullpath = Context.GetScriptFileFullPath(path)
+				return require('devrequire')({
+					get_change : function (watcher) {
+						var full_files = files.map(function (x) { return Context.GetScriptFileFullPath(x)})
+						return full_files.filter(function (x) {return watcher.Contains(x)})                
+					},
+					exec : function () {
+						var design = test()
+						files = design.$files
+						return design
+					},
+					notify : true,
+					message : "Live reload (jade)"
+				})
+			}    
+			
+			var live_jade = livereload(view,function () {
+				return test(view)
+			})
+				
+			page.$destroy = live_jade
+		
+			return page	
+		}
+		
+		return main				
+	}	
+	
+	module.exports = liveapp;
+}())
diff --git a/Plugins/UnrealJS/Content/Scripts/lodash.js b/Plugins/UnrealJS/Content/Scripts/lodash.js
new file mode 100644
index 0000000..c05fade
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/lodash.js
@@ -0,0 +1,12325 @@
+/**
+ * @license
+ * lodash 3.10.1 (Custom Build) <https://lodash.com/>
+ * Build: `lodash modern -o ./lodash.js`
+ * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <https://lodash.com/license>
+ */
+;(function() {
+
+  /** Used as a safe reference for `undefined` in pre-ES5 environments. */
+  var undefined;
+
+  /** Used as the semantic version number. */
+  var VERSION = '3.10.1';
+
+  /** Used to compose bitmasks for wrapper metadata. */
+  var BIND_FLAG = 1,
+      BIND_KEY_FLAG = 2,
+      CURRY_BOUND_FLAG = 4,
+      CURRY_FLAG = 8,
+      CURRY_RIGHT_FLAG = 16,
+      PARTIAL_FLAG = 32,
+      PARTIAL_RIGHT_FLAG = 64,
+      ARY_FLAG = 128,
+      REARG_FLAG = 256;
+
+  /** Used as default options for `_.trunc`. */
+  var DEFAULT_TRUNC_LENGTH = 30,
+      DEFAULT_TRUNC_OMISSION = '...';
+
+  /** Used to detect when a function becomes hot. */
+  var HOT_COUNT = 150,
+      HOT_SPAN = 16;
+
+  /** Used as the size to enable large array optimizations. */
+  var LARGE_ARRAY_SIZE = 200;
+
+  /** Used to indicate the type of lazy iteratees. */
+  var LAZY_FILTER_FLAG = 1,
+      LAZY_MAP_FLAG = 2;
+
+  /** Used as the `TypeError` message for "Functions" methods. */
+  var FUNC_ERROR_TEXT = 'Expected a function';
+
+  /** Used as the internal argument placeholder. */
+  var PLACEHOLDER = '__lodash_placeholder__';
+
+  /** `Object#toString` result references. */
+  var argsTag = '[object Arguments]',
+      arrayTag = '[object Array]',
+      boolTag = '[object Boolean]',
+      dateTag = '[object Date]',
+      errorTag = '[object Error]',
+      funcTag = '[object Function]',
+      mapTag = '[object Map]',
+      numberTag = '[object Number]',
+      objectTag = '[object Object]',
+      regexpTag = '[object RegExp]',
+      setTag = '[object Set]',
+      stringTag = '[object String]',
+      weakMapTag = '[object WeakMap]';
+
+  var arrayBufferTag = '[object ArrayBuffer]',
+      float32Tag = '[object Float32Array]',
+      float64Tag = '[object Float64Array]',
+      int8Tag = '[object Int8Array]',
+      int16Tag = '[object Int16Array]',
+      int32Tag = '[object Int32Array]',
+      uint8Tag = '[object Uint8Array]',
+      uint8ClampedTag = '[object Uint8ClampedArray]',
+      uint16Tag = '[object Uint16Array]',
+      uint32Tag = '[object Uint32Array]';
+
+  /** Used to match empty string literals in compiled template source. */
+  var reEmptyStringLeading = /\b__p \+= '';/g,
+      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+  /** Used to match HTML entities and HTML characters. */
+  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g,
+      reUnescapedHtml = /[&<>"'`]/g,
+      reHasEscapedHtml = RegExp(reEscapedHtml.source),
+      reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
+
+  /** Used to match template delimiters. */
+  var reEscape = /<%-([\s\S]+?)%>/g,
+      reEvaluate = /<%([\s\S]+?)%>/g,
+      reInterpolate = /<%=([\s\S]+?)%>/g;
+
+  /** Used to match property names within property paths. */
+  var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,
+      reIsPlainProp = /^\w*$/,
+      rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g;
+
+  /**
+   * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns)
+   * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern).
+   */
+  var reRegExpChars = /^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,
+      reHasRegExpChars = RegExp(reRegExpChars.source);
+
+  /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */
+  var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g;
+
+  /** Used to match backslashes in property paths. */
+  var reEscapeChar = /\\(\\)?/g;
+
+  /** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */
+  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+  /** Used to match `RegExp` flags from their coerced string values. */
+  var reFlags = /\w*$/;
+
+  /** Used to detect hexadecimal string values. */
+  var reHasHexPrefix = /^0[xX]/;
+
+  /** Used to detect host constructors (Safari > 5). */
+  var reIsHostCtor = /^\[object .+?Constructor\]$/;
+
+  /** Used to detect unsigned integer values. */
+  var reIsUint = /^\d+$/;
+
+  /** Used to match latin-1 supplementary letters (excluding mathematical operators). */
+  var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;
+
+  /** Used to ensure capturing order of template delimiters. */
+  var reNoMatch = /($^)/;
+
+  /** Used to match unescaped characters in compiled string literals. */
+  var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
+
+  /** Used to match words to create compound words. */
+  var reWords = (function() {
+    var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]',
+        lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+';
+
+    return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g');
+  }());
+
+  /** Used to assign default `context` object properties. */
+  var contextProps = [
+    'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array',
+    'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number',
+    'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite',
+    'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array',
+    'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap'
+  ];
+
+  /** Used to make template sourceURLs easier to identify. */
+  var templateCounter = -1;
+
+  /** Used to identify `toStringTag` values of typed arrays. */
+  var typedArrayTags = {};
+  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+  typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+  typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+  typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+  typedArrayTags[uint32Tag] = true;
+  typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+  typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+  typedArrayTags[dateTag] = typedArrayTags[errorTag] =
+  typedArrayTags[funcTag] = typedArrayTags[mapTag] =
+  typedArrayTags[numberTag] = typedArrayTags[objectTag] =
+  typedArrayTags[regexpTag] = typedArrayTags[setTag] =
+  typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
+
+  /** Used to identify `toStringTag` values supported by `_.clone`. */
+  var cloneableTags = {};
+  cloneableTags[argsTag] = cloneableTags[arrayTag] =
+  cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
+  cloneableTags[dateTag] = cloneableTags[float32Tag] =
+  cloneableTags[float64Tag] = cloneableTags[int8Tag] =
+  cloneableTags[int16Tag] = cloneableTags[int32Tag] =
+  cloneableTags[numberTag] = cloneableTags[objectTag] =
+  cloneableTags[regexpTag] = cloneableTags[stringTag] =
+  cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+  cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+  cloneableTags[errorTag] = cloneableTags[funcTag] =
+  cloneableTags[mapTag] = cloneableTags[setTag] =
+  cloneableTags[weakMapTag] = false;
+
+  /** Used to map latin-1 supplementary letters to basic latin letters. */
+  var deburredLetters = {
+    '\xc0': 'A',  '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
+    '\xe0': 'a',  '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
+    '\xc7': 'C',  '\xe7': 'c',
+    '\xd0': 'D',  '\xf0': 'd',
+    '\xc8': 'E',  '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
+    '\xe8': 'e',  '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
+    '\xcC': 'I',  '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
+    '\xeC': 'i',  '\xed': 'i', '\xee': 'i', '\xef': 'i',
+    '\xd1': 'N',  '\xf1': 'n',
+    '\xd2': 'O',  '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
+    '\xf2': 'o',  '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
+    '\xd9': 'U',  '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
+    '\xf9': 'u',  '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
+    '\xdd': 'Y',  '\xfd': 'y', '\xff': 'y',
+    '\xc6': 'Ae', '\xe6': 'ae',
+    '\xde': 'Th', '\xfe': 'th',
+    '\xdf': 'ss'
+  };
+
+  /** Used to map characters to HTML entities. */
+  var htmlEscapes = {
+    '&': '&amp;',
+    '<': '&lt;',
+    '>': '&gt;',
+    '"': '&quot;',
+    "'": '&#39;',
+    '`': '&#96;'
+  };
+
+  /** Used to map HTML entities to characters. */
+  var htmlUnescapes = {
+    '&amp;': '&',
+    '&lt;': '<',
+    '&gt;': '>',
+    '&quot;': '"',
+    '&#39;': "'",
+    '&#96;': '`'
+  };
+
+  /** Used to determine if values are of the language type `Object`. */
+  var objectTypes = {
+    'function': true,
+    'object': true
+  };
+
+  /** Used to escape characters for inclusion in compiled regexes. */
+  var regexpEscapes = {
+    '0': 'x30', '1': 'x31', '2': 'x32', '3': 'x33', '4': 'x34',
+    '5': 'x35', '6': 'x36', '7': 'x37', '8': 'x38', '9': 'x39',
+    'A': 'x41', 'B': 'x42', 'C': 'x43', 'D': 'x44', 'E': 'x45', 'F': 'x46',
+    'a': 'x61', 'b': 'x62', 'c': 'x63', 'd': 'x64', 'e': 'x65', 'f': 'x66',
+    'n': 'x6e', 'r': 'x72', 't': 'x74', 'u': 'x75', 'v': 'x76', 'x': 'x78'
+  };
+
+  /** Used to escape characters for inclusion in compiled string literals. */
+  var stringEscapes = {
+    '\\': '\\',
+    "'": "'",
+    '\n': 'n',
+    '\r': 'r',
+    '\u2028': 'u2028',
+    '\u2029': 'u2029'
+  };
+
+  /** Detect free variable `exports`. */
+  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+  /** Detect free variable `module`. */
+  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+  /** Detect free variable `global` from Node.js. */
+  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
+
+  /** Detect free variable `self`. */
+  var freeSelf = objectTypes[typeof self] && self && self.Object && self;
+
+  /** Detect free variable `window`. */
+  var freeWindow = objectTypes[typeof window] && window && window.Object && window;
+
+  /** Detect the popular CommonJS extension `module.exports`. */
+  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+  /**
+   * Used as a reference to the global object.
+   *
+   * The `this` value is used if it's the global object to avoid Greasemonkey's
+   * restricted `window` object, otherwise the `window` object is used.
+   */
+  var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * The base implementation of `compareAscending` which compares values and
+   * sorts them in ascending order without guaranteeing a stable sort.
+   *
+   * @private
+   * @param {*} value The value to compare.
+   * @param {*} other The other value to compare.
+   * @returns {number} Returns the sort order indicator for `value`.
+   */
+  function baseCompareAscending(value, other) {
+    if (value !== other) {
+      var valIsNull = value === null,
+          valIsUndef = value === undefined,
+          valIsReflexive = value === value;
+
+      var othIsNull = other === null,
+          othIsUndef = other === undefined,
+          othIsReflexive = other === other;
+
+      if ((value > other && !othIsNull) || !valIsReflexive ||
+          (valIsNull && !othIsUndef && othIsReflexive) ||
+          (valIsUndef && othIsReflexive)) {
+        return 1;
+      }
+      if ((value < other && !valIsNull) || !othIsReflexive ||
+          (othIsNull && !valIsUndef && valIsReflexive) ||
+          (othIsUndef && valIsReflexive)) {
+        return -1;
+      }
+    }
+    return 0;
+  }
+
+  /**
+   * The base implementation of `_.findIndex` and `_.findLastIndex` without
+   * support for callback shorthands and `this` binding.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {Function} predicate The function invoked per iteration.
+   * @param {boolean} [fromRight] Specify iterating from right to left.
+   * @returns {number} Returns the index of the matched value, else `-1`.
+   */
+  function baseFindIndex(array, predicate, fromRight) {
+    var length = array.length,
+        index = fromRight ? length : -1;
+
+    while ((fromRight ? index-- : ++index < length)) {
+      if (predicate(array[index], index, array)) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * The base implementation of `_.indexOf` without support for binary searches.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {*} value The value to search for.
+   * @param {number} fromIndex The index to search from.
+   * @returns {number} Returns the index of the matched value, else `-1`.
+   */
+  function baseIndexOf(array, value, fromIndex) {
+    if (value !== value) {
+      return indexOfNaN(array, fromIndex);
+    }
+    var index = fromIndex - 1,
+        length = array.length;
+
+    while (++index < length) {
+      if (array[index] === value) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * The base implementation of `_.isFunction` without support for environments
+   * with incorrect `typeof` results.
+   *
+   * @private
+   * @param {*} value The value to check.
+   * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+   */
+  function baseIsFunction(value) {
+    // Avoid a Chakra JIT bug in compatibility modes of IE 11.
+    // See https://github.com/jashkenas/underscore/issues/1621 for more details.
+    return typeof value == 'function' || false;
+  }
+
+  /**
+   * Converts `value` to a string if it's not one. An empty string is returned
+   * for `null` or `undefined` values.
+   *
+   * @private
+   * @param {*} value The value to process.
+   * @returns {string} Returns the string.
+   */
+  function baseToString(value) {
+    return value == null ? '' : (value + '');
+  }
+
+  /**
+   * Used by `_.trim` and `_.trimLeft` to get the index of the first character
+   * of `string` that is not found in `chars`.
+   *
+   * @private
+   * @param {string} string The string to inspect.
+   * @param {string} chars The characters to find.
+   * @returns {number} Returns the index of the first character not found in `chars`.
+   */
+  function charsLeftIndex(string, chars) {
+    var index = -1,
+        length = string.length;
+
+    while (++index < length && chars.indexOf(string.charAt(index)) > -1) {}
+    return index;
+  }
+
+  /**
+   * Used by `_.trim` and `_.trimRight` to get the index of the last character
+   * of `string` that is not found in `chars`.
+   *
+   * @private
+   * @param {string} string The string to inspect.
+   * @param {string} chars The characters to find.
+   * @returns {number} Returns the index of the last character not found in `chars`.
+   */
+  function charsRightIndex(string, chars) {
+    var index = string.length;
+
+    while (index-- && chars.indexOf(string.charAt(index)) > -1) {}
+    return index;
+  }
+
+  /**
+   * Used by `_.sortBy` to compare transformed elements of a collection and stable
+   * sort them in ascending order.
+   *
+   * @private
+   * @param {Object} object The object to compare.
+   * @param {Object} other The other object to compare.
+   * @returns {number} Returns the sort order indicator for `object`.
+   */
+  function compareAscending(object, other) {
+    return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index);
+  }
+
+  /**
+   * Used by `_.sortByOrder` to compare multiple properties of a value to another
+   * and stable sort them.
+   *
+   * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise,
+   * a value is sorted in ascending order if its corresponding order is "asc", and
+   * descending if "desc".
+   *
+   * @private
+   * @param {Object} object The object to compare.
+   * @param {Object} other The other object to compare.
+   * @param {boolean[]} orders The order to sort by for each property.
+   * @returns {number} Returns the sort order indicator for `object`.
+   */
+  function compareMultiple(object, other, orders) {
+    var index = -1,
+        objCriteria = object.criteria,
+        othCriteria = other.criteria,
+        length = objCriteria.length,
+        ordersLength = orders.length;
+
+    while (++index < length) {
+      var result = baseCompareAscending(objCriteria[index], othCriteria[index]);
+      if (result) {
+        if (index >= ordersLength) {
+          return result;
+        }
+        var order = orders[index];
+        return result * ((order === 'asc' || order === true) ? 1 : -1);
+      }
+    }
+    // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+    // that causes it, under certain circumstances, to provide the same value for
+    // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+    // for more details.
+    //
+    // This also ensures a stable sort in V8 and other engines.
+    // See https://code.google.com/p/v8/issues/detail?id=90 for more details.
+    return object.index - other.index;
+  }
+
+  /**
+   * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters.
+   *
+   * @private
+   * @param {string} letter The matched letter to deburr.
+   * @returns {string} Returns the deburred letter.
+   */
+  function deburrLetter(letter) {
+    return deburredLetters[letter];
+  }
+
+  /**
+   * Used by `_.escape` to convert characters to HTML entities.
+   *
+   * @private
+   * @param {string} chr The matched character to escape.
+   * @returns {string} Returns the escaped character.
+   */
+  function escapeHtmlChar(chr) {
+    return htmlEscapes[chr];
+  }
+
+  /**
+   * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes.
+   *
+   * @private
+   * @param {string} chr The matched character to escape.
+   * @param {string} leadingChar The capture group for a leading character.
+   * @param {string} whitespaceChar The capture group for a whitespace character.
+   * @returns {string} Returns the escaped character.
+   */
+  function escapeRegExpChar(chr, leadingChar, whitespaceChar) {
+    if (leadingChar) {
+      chr = regexpEscapes[chr];
+    } else if (whitespaceChar) {
+      chr = stringEscapes[chr];
+    }
+    return '\\' + chr;
+  }
+
+  /**
+   * Used by `_.template` to escape characters for inclusion in compiled string literals.
+   *
+   * @private
+   * @param {string} chr The matched character to escape.
+   * @returns {string} Returns the escaped character.
+   */
+  function escapeStringChar(chr) {
+    return '\\' + stringEscapes[chr];
+  }
+
+  /**
+   * Gets the index at which the first occurrence of `NaN` is found in `array`.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {number} fromIndex The index to search from.
+   * @param {boolean} [fromRight] Specify iterating from right to left.
+   * @returns {number} Returns the index of the matched `NaN`, else `-1`.
+   */
+  function indexOfNaN(array, fromIndex, fromRight) {
+    var length = array.length,
+        index = fromIndex + (fromRight ? 0 : -1);
+
+    while ((fromRight ? index-- : ++index < length)) {
+      var other = array[index];
+      if (other !== other) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Checks if `value` is object-like.
+   *
+   * @private
+   * @param {*} value The value to check.
+   * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+   */
+  function isObjectLike(value) {
+    return !!value && typeof value == 'object';
+  }
+
+  /**
+   * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a
+   * character code is whitespace.
+   *
+   * @private
+   * @param {number} charCode The character code to inspect.
+   * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`.
+   */
+  function isSpace(charCode) {
+    return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 ||
+      (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279)));
+  }
+
+  /**
+   * Replaces all `placeholder` elements in `array` with an internal placeholder
+   * and returns an array of their indexes.
+   *
+   * @private
+   * @param {Array} array The array to modify.
+   * @param {*} placeholder The placeholder to replace.
+   * @returns {Array} Returns the new array of placeholder indexes.
+   */
+  function replaceHolders(array, placeholder) {
+    var index = -1,
+        length = array.length,
+        resIndex = -1,
+        result = [];
+
+    while (++index < length) {
+      if (array[index] === placeholder) {
+        array[index] = PLACEHOLDER;
+        result[++resIndex] = index;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * An implementation of `_.uniq` optimized for sorted arrays without support
+   * for callback shorthands and `this` binding.
+   *
+   * @private
+   * @param {Array} array The array to inspect.
+   * @param {Function} [iteratee] The function invoked per iteration.
+   * @returns {Array} Returns the new duplicate free array.
+   */
+  function sortedUniq(array, iteratee) {
+    var seen,
+        index = -1,
+        length = array.length,
+        resIndex = -1,
+        result = [];
+
+    while (++index < length) {
+      var value = array[index],
+          computed = iteratee ? iteratee(value, index, array) : value;
+
+      if (!index || seen !== computed) {
+        seen = computed;
+        result[++resIndex] = value;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace
+   * character of `string`.
+   *
+   * @private
+   * @param {string} string The string to inspect.
+   * @returns {number} Returns the index of the first non-whitespace character.
+   */
+  function trimmedLeftIndex(string) {
+    var index = -1,
+        length = string.length;
+
+    while (++index < length && isSpace(string.charCodeAt(index))) {}
+    return index;
+  }
+
+  /**
+   * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace
+   * character of `string`.
+   *
+   * @private
+   * @param {string} string The string to inspect.
+   * @returns {number} Returns the index of the last non-whitespace character.
+   */
+  function trimmedRightIndex(string) {
+    var index = string.length;
+
+    while (index-- && isSpace(string.charCodeAt(index))) {}
+    return index;
+  }
+
+  /**
+   * Used by `_.unescape` to convert HTML entities to characters.
+   *
+   * @private
+   * @param {string} chr The matched character to unescape.
+   * @returns {string} Returns the unescaped character.
+   */
+  function unescapeHtmlChar(chr) {
+    return htmlUnescapes[chr];
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Create a new pristine `lodash` function using the given `context` object.
+   *
+   * @static
+   * @memberOf _
+   * @category Utility
+   * @param {Object} [context=root] The context object.
+   * @returns {Function} Returns a new `lodash` function.
+   * @example
+   *
+   * _.mixin({ 'foo': _.constant('foo') });
+   *
+   * var lodash = _.runInContext();
+   * lodash.mixin({ 'bar': lodash.constant('bar') });
+   *
+   * _.isFunction(_.foo);
+   * // => true
+   * _.isFunction(_.bar);
+   * // => false
+   *
+   * lodash.isFunction(lodash.foo);
+   * // => false
+   * lodash.isFunction(lodash.bar);
+   * // => true
+   *
+   * // using `context` to mock `Date#getTime` use in `_.now`
+   * var mock = _.runInContext({
+   *   'Date': function() {
+   *     return { 'getTime': getTimeMock };
+   *   }
+   * });
+   *
+   * // or creating a suped-up `defer` in Node.js
+   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;
+   */
+  function runInContext(context) {
+    // Avoid issues with some ES3 environments that attempt to use values, named
+    // after built-in constructors like `Object`, for the creation of literals.
+    // ES5 clears this up by stating that literals must use built-in constructors.
+    // See https://es5.github.io/#x11.1.5 for more details.
+    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+    /** Native constructor references. */
+    var Array = context.Array,
+        Date = context.Date,
+        Error = context.Error,
+        Function = context.Function,
+        Math = context.Math,
+        Number = context.Number,
+        Object = context.Object,
+        RegExp = context.RegExp,
+        String = context.String,
+        TypeError = context.TypeError;
+
+    /** Used for native method references. */
+    var arrayProto = Array.prototype,
+        objectProto = Object.prototype,
+        stringProto = String.prototype;
+
+    /** Used to resolve the decompiled source of functions. */
+    var fnToString = Function.prototype.toString;
+
+    /** Used to check objects for own properties. */
+    var hasOwnProperty = objectProto.hasOwnProperty;
+
+    /** Used to generate unique IDs. */
+    var idCounter = 0;
+
+    /**
+     * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
+     * of values.
+     */
+    var objToString = objectProto.toString;
+
+    /** Used to restore the original `_` reference in `_.noConflict`. */
+    var oldDash = root._;
+
+    /** Used to detect if a method is native. */
+    var reIsNative = RegExp('^' +
+      fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
+      .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+    );
+
+    /** Native method references. */
+    var ArrayBuffer = context.ArrayBuffer,
+        clearTimeout = context.clearTimeout,
+        parseFloat = context.parseFloat,
+        pow = Math.pow,
+        propertyIsEnumerable = objectProto.propertyIsEnumerable,
+        Set = getNative(context, 'Set'),
+        setTimeout = context.setTimeout,
+        splice = arrayProto.splice,
+        Uint8Array = context.Uint8Array,
+        WeakMap = getNative(context, 'WeakMap');
+
+    /* Native method references for those with the same name as other `lodash` methods. */
+    var nativeCeil = Math.ceil,
+        nativeCreate = getNative(Object, 'create'),
+        nativeFloor = Math.floor,
+        nativeIsArray = getNative(Array, 'isArray'),
+        nativeIsFinite = context.isFinite,
+        nativeKeys = getNative(Object, 'keys'),
+        nativeMax = Math.max,
+        nativeMin = Math.min,
+        nativeNow = getNative(Date, 'now'),
+        nativeParseInt = context.parseInt,
+        nativeRandom = Math.random;
+
+    /** Used as references for `-Infinity` and `Infinity`. */
+    var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY,
+        POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
+
+    /** Used as references for the maximum length and index of an array. */
+    var MAX_ARRAY_LENGTH = 4294967295,
+        MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
+        HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
+
+    /**
+     * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
+     * of an array-like value.
+     */
+    var MAX_SAFE_INTEGER = 9007199254740991;
+
+    /** Used to store function metadata. */
+    var metaMap = WeakMap && new WeakMap;
+
+    /** Used to lookup unminified function names. */
+    var realNames = {};
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object which wraps `value` to enable implicit chaining.
+     * Methods that operate on and return arrays, collections, and functions can
+     * be chained together. Methods that retrieve a single value or may return a
+     * primitive value will automatically end the chain returning the unwrapped
+     * value. Explicit chaining may be enabled using `_.chain`. The execution of
+     * chained methods is lazy, that is, execution is deferred until `_#value`
+     * is implicitly or explicitly called.
+     *
+     * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
+     * fusion is an optimization strategy which merge iteratee calls; this can help
+     * to avoid the creation of intermediate data structures and greatly reduce the
+     * number of iteratee executions.
+     *
+     * Chaining is supported in custom builds as long as the `_#value` method is
+     * directly or indirectly included in the build.
+     *
+     * In addition to lodash methods, wrappers have `Array` and `String` methods.
+     *
+     * The wrapper `Array` methods are:
+     * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
+     * `splice`, and `unshift`
+     *
+     * The wrapper `String` methods are:
+     * `replace` and `split`
+     *
+     * The wrapper methods that support shortcut fusion are:
+     * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
+     * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
+     * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
+     * and `where`
+     *
+     * The chainable wrapper methods are:
+     * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
+     * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
+     * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`,
+     * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`,
+     * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`,
+     * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+     * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+     * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`,
+     * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`,
+     * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`,
+     * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`,
+     * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`,
+     * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`,
+     * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`,
+     * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`,
+     * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`,
+     * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith`
+     *
+     * The wrapper methods that are **not** chainable by default are:
+     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`,
+     * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`,
+     * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`,
+     * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`,
+     * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+     * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`,
+     * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`,
+     * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`,
+     * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`,
+     * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`,
+     * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`,
+     * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`,
+     * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`,
+     * `unescape`, `uniqueId`, `value`, and `words`
+     *
+     * The wrapper method `sample` will return a wrapped value when `n` is provided,
+     * otherwise an unwrapped value is returned.
+     *
+     * @name _
+     * @constructor
+     * @category Chain
+     * @param {*} value The value to wrap in a `lodash` instance.
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var wrapped = _([1, 2, 3]);
+     *
+     * // returns an unwrapped value
+     * wrapped.reduce(function(total, n) {
+     *   return total + n;
+     * });
+     * // => 6
+     *
+     * // returns a wrapped value
+     * var squares = wrapped.map(function(n) {
+     *   return n * n;
+     * });
+     *
+     * _.isArray(squares);
+     * // => false
+     *
+     * _.isArray(squares.value());
+     * // => true
+     */
+    function lodash(value) {
+      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
+        if (value instanceof LodashWrapper) {
+          return value;
+        }
+        if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
+          return wrapperClone(value);
+        }
+      }
+      return new LodashWrapper(value);
+    }
+
+    /**
+     * The function whose prototype all chaining wrappers inherit from.
+     *
+     * @private
+     */
+    function baseLodash() {
+      // No operation performed.
+    }
+
+    /**
+     * The base constructor for creating `lodash` wrapper objects.
+     *
+     * @private
+     * @param {*} value The value to wrap.
+     * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
+     * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
+     */
+    function LodashWrapper(value, chainAll, actions) {
+      this.__wrapped__ = value;
+      this.__actions__ = actions || [];
+      this.__chain__ = !!chainAll;
+    }
+
+    /**
+     * An object environment feature flags.
+     *
+     * @static
+     * @memberOf _
+     * @type Object
+     */
+    var support = lodash.support = {};
+
+    /**
+     * By default, the template delimiters used by lodash are like those in
+     * embedded Ruby (ERB). Change the following template settings to use
+     * alternative delimiters.
+     *
+     * @static
+     * @memberOf _
+     * @type Object
+     */
+    lodash.templateSettings = {
+
+      /**
+       * Used to detect `data` property values to be HTML-escaped.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'escape': reEscape,
+
+      /**
+       * Used to detect code to be evaluated.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'evaluate': reEvaluate,
+
+      /**
+       * Used to detect `data` property values to inject.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'interpolate': reInterpolate,
+
+      /**
+       * Used to reference the data object in the template text.
+       *
+       * @memberOf _.templateSettings
+       * @type string
+       */
+      'variable': '',
+
+      /**
+       * Used to import variables into the compiled template.
+       *
+       * @memberOf _.templateSettings
+       * @type Object
+       */
+      'imports': {
+
+        /**
+         * A reference to the `lodash` function.
+         *
+         * @memberOf _.templateSettings.imports
+         * @type Function
+         */
+        '_': lodash
+      }
+    };
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+     *
+     * @private
+     * @param {*} value The value to wrap.
+     */
+    function LazyWrapper(value) {
+      this.__wrapped__ = value;
+      this.__actions__ = [];
+      this.__dir__ = 1;
+      this.__filtered__ = false;
+      this.__iteratees__ = [];
+      this.__takeCount__ = POSITIVE_INFINITY;
+      this.__views__ = [];
+    }
+
+    /**
+     * Creates a clone of the lazy wrapper object.
+     *
+     * @private
+     * @name clone
+     * @memberOf LazyWrapper
+     * @returns {Object} Returns the cloned `LazyWrapper` object.
+     */
+    function lazyClone() {
+      var result = new LazyWrapper(this.__wrapped__);
+      result.__actions__ = arrayCopy(this.__actions__);
+      result.__dir__ = this.__dir__;
+      result.__filtered__ = this.__filtered__;
+      result.__iteratees__ = arrayCopy(this.__iteratees__);
+      result.__takeCount__ = this.__takeCount__;
+      result.__views__ = arrayCopy(this.__views__);
+      return result;
+    }
+
+    /**
+     * Reverses the direction of lazy iteration.
+     *
+     * @private
+     * @name reverse
+     * @memberOf LazyWrapper
+     * @returns {Object} Returns the new reversed `LazyWrapper` object.
+     */
+    function lazyReverse() {
+      if (this.__filtered__) {
+        var result = new LazyWrapper(this);
+        result.__dir__ = -1;
+        result.__filtered__ = true;
+      } else {
+        result = this.clone();
+        result.__dir__ *= -1;
+      }
+      return result;
+    }
+
+    /**
+     * Extracts the unwrapped value from its lazy wrapper.
+     *
+     * @private
+     * @name value
+     * @memberOf LazyWrapper
+     * @returns {*} Returns the unwrapped value.
+     */
+    function lazyValue() {
+      var array = this.__wrapped__.value(),
+          dir = this.__dir__,
+          isArr = isArray(array),
+          isRight = dir < 0,
+          arrLength = isArr ? array.length : 0,
+          view = getView(0, arrLength, this.__views__),
+          start = view.start,
+          end = view.end,
+          length = end - start,
+          index = isRight ? end : (start - 1),
+          iteratees = this.__iteratees__,
+          iterLength = iteratees.length,
+          resIndex = 0,
+          takeCount = nativeMin(length, this.__takeCount__);
+
+      if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) {
+        return baseWrapperValue(array, this.__actions__);
+      }
+      var result = [];
+
+      outer:
+      while (length-- && resIndex < takeCount) {
+        index += dir;
+
+        var iterIndex = -1,
+            value = array[index];
+
+        while (++iterIndex < iterLength) {
+          var data = iteratees[iterIndex],
+              iteratee = data.iteratee,
+              type = data.type,
+              computed = iteratee(value);
+
+          if (type == LAZY_MAP_FLAG) {
+            value = computed;
+          } else if (!computed) {
+            if (type == LAZY_FILTER_FLAG) {
+              continue outer;
+            } else {
+              break outer;
+            }
+          }
+        }
+        result[resIndex++] = value;
+      }
+      return result;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a cache object to store key/value pairs.
+     *
+     * @private
+     * @static
+     * @name Cache
+     * @memberOf _.memoize
+     */
+    function MapCache() {
+      this.__data__ = {};
+    }
+
+    /**
+     * Removes `key` and its value from the cache.
+     *
+     * @private
+     * @name delete
+     * @memberOf _.memoize.Cache
+     * @param {string} key The key of the value to remove.
+     * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`.
+     */
+    function mapDelete(key) {
+      return this.has(key) && delete this.__data__[key];
+    }
+
+    /**
+     * Gets the cached value for `key`.
+     *
+     * @private
+     * @name get
+     * @memberOf _.memoize.Cache
+     * @param {string} key The key of the value to get.
+     * @returns {*} Returns the cached value.
+     */
+    function mapGet(key) {
+      return key == '__proto__' ? undefined : this.__data__[key];
+    }
+
+    /**
+     * Checks if a cached value for `key` exists.
+     *
+     * @private
+     * @name has
+     * @memberOf _.memoize.Cache
+     * @param {string} key The key of the entry to check.
+     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+     */
+    function mapHas(key) {
+      return key != '__proto__' && hasOwnProperty.call(this.__data__, key);
+    }
+
+    /**
+     * Sets `value` to `key` of the cache.
+     *
+     * @private
+     * @name set
+     * @memberOf _.memoize.Cache
+     * @param {string} key The key of the value to cache.
+     * @param {*} value The value to cache.
+     * @returns {Object} Returns the cache object.
+     */
+    function mapSet(key, value) {
+      if (key != '__proto__') {
+        this.__data__[key] = value;
+      }
+      return this;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     *
+     * Creates a cache object to store unique values.
+     *
+     * @private
+     * @param {Array} [values] The values to cache.
+     */
+    function SetCache(values) {
+      var length = values ? values.length : 0;
+
+      this.data = { 'hash': nativeCreate(null), 'set': new Set };
+      while (length--) {
+        this.push(values[length]);
+      }
+    }
+
+    /**
+     * Checks if `value` is in `cache` mimicking the return signature of
+     * `_.indexOf` by returning `0` if the value is found, else `-1`.
+     *
+     * @private
+     * @param {Object} cache The cache to search.
+     * @param {*} value The value to search for.
+     * @returns {number} Returns `0` if `value` is found, else `-1`.
+     */
+    function cacheIndexOf(cache, value) {
+      var data = cache.data,
+          result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value];
+
+      return result ? 0 : -1;
+    }
+
+    /**
+     * Adds `value` to the cache.
+     *
+     * @private
+     * @name push
+     * @memberOf SetCache
+     * @param {*} value The value to cache.
+     */
+    function cachePush(value) {
+      var data = this.data;
+      if (typeof value == 'string' || isObject(value)) {
+        data.set.add(value);
+      } else {
+        data.hash[value] = true;
+      }
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a new array joining `array` with `other`.
+     *
+     * @private
+     * @param {Array} array The array to join.
+     * @param {Array} other The other array to join.
+     * @returns {Array} Returns the new concatenated array.
+     */
+    function arrayConcat(array, other) {
+      var index = -1,
+          length = array.length,
+          othIndex = -1,
+          othLength = other.length,
+          result = Array(length + othLength);
+
+      while (++index < length) {
+        result[index] = array[index];
+      }
+      while (++othIndex < othLength) {
+        result[index++] = other[othIndex];
+      }
+      return result;
+    }
+
+    /**
+     * Copies the values of `source` to `array`.
+     *
+     * @private
+     * @param {Array} source The array to copy values from.
+     * @param {Array} [array=[]] The array to copy values to.
+     * @returns {Array} Returns `array`.
+     */
+    function arrayCopy(source, array) {
+      var index = -1,
+          length = source.length;
+
+      array || (array = Array(length));
+      while (++index < length) {
+        array[index] = source[index];
+      }
+      return array;
+    }
+
+    /**
+     * A specialized version of `_.forEach` for arrays without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array} Returns `array`.
+     */
+    function arrayEach(array, iteratee) {
+      var index = -1,
+          length = array.length;
+
+      while (++index < length) {
+        if (iteratee(array[index], index, array) === false) {
+          break;
+        }
+      }
+      return array;
+    }
+
+    /**
+     * A specialized version of `_.forEachRight` for arrays without support for
+     * callback shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array} Returns `array`.
+     */
+    function arrayEachRight(array, iteratee) {
+      var length = array.length;
+
+      while (length--) {
+        if (iteratee(array[length], length, array) === false) {
+          break;
+        }
+      }
+      return array;
+    }
+
+    /**
+     * A specialized version of `_.every` for arrays without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {boolean} Returns `true` if all elements pass the predicate check,
+     *  else `false`.
+     */
+    function arrayEvery(array, predicate) {
+      var index = -1,
+          length = array.length;
+
+      while (++index < length) {
+        if (!predicate(array[index], index, array)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * A specialized version of `baseExtremum` for arrays which invokes `iteratee`
+     * with one argument: (value).
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {Function} comparator The function used to compare values.
+     * @param {*} exValue The initial extremum value.
+     * @returns {*} Returns the extremum value.
+     */
+    function arrayExtremum(array, iteratee, comparator, exValue) {
+      var index = -1,
+          length = array.length,
+          computed = exValue,
+          result = computed;
+
+      while (++index < length) {
+        var value = array[index],
+            current = +iteratee(value);
+
+        if (comparator(current, computed)) {
+          computed = current;
+          result = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * A specialized version of `_.filter` for arrays without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {Array} Returns the new filtered array.
+     */
+    function arrayFilter(array, predicate) {
+      var index = -1,
+          length = array.length,
+          resIndex = -1,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+        if (predicate(value, index, array)) {
+          result[++resIndex] = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * A specialized version of `_.map` for arrays without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array} Returns the new mapped array.
+     */
+    function arrayMap(array, iteratee) {
+      var index = -1,
+          length = array.length,
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = iteratee(array[index], index, array);
+      }
+      return result;
+    }
+
+    /**
+     * Appends the elements of `values` to `array`.
+     *
+     * @private
+     * @param {Array} array The array to modify.
+     * @param {Array} values The values to append.
+     * @returns {Array} Returns `array`.
+     */
+    function arrayPush(array, values) {
+      var index = -1,
+          length = values.length,
+          offset = array.length;
+
+      while (++index < length) {
+        array[offset + index] = values[index];
+      }
+      return array;
+    }
+
+    /**
+     * A specialized version of `_.reduce` for arrays without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {*} [accumulator] The initial value.
+     * @param {boolean} [initFromArray] Specify using the first element of `array`
+     *  as the initial value.
+     * @returns {*} Returns the accumulated value.
+     */
+    function arrayReduce(array, iteratee, accumulator, initFromArray) {
+      var index = -1,
+          length = array.length;
+
+      if (initFromArray && length) {
+        accumulator = array[++index];
+      }
+      while (++index < length) {
+        accumulator = iteratee(accumulator, array[index], index, array);
+      }
+      return accumulator;
+    }
+
+    /**
+     * A specialized version of `_.reduceRight` for arrays without support for
+     * callback shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {*} [accumulator] The initial value.
+     * @param {boolean} [initFromArray] Specify using the last element of `array`
+     *  as the initial value.
+     * @returns {*} Returns the accumulated value.
+     */
+    function arrayReduceRight(array, iteratee, accumulator, initFromArray) {
+      var length = array.length;
+      if (initFromArray && length) {
+        accumulator = array[--length];
+      }
+      while (length--) {
+        accumulator = iteratee(accumulator, array[length], length, array);
+      }
+      return accumulator;
+    }
+
+    /**
+     * A specialized version of `_.some` for arrays without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {boolean} Returns `true` if any element passes the predicate check,
+     *  else `false`.
+     */
+    function arraySome(array, predicate) {
+      var index = -1,
+          length = array.length;
+
+      while (++index < length) {
+        if (predicate(array[index], index, array)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /**
+     * A specialized version of `_.sum` for arrays without support for callback
+     * shorthands and `this` binding..
+     *
+     * @private
+     * @param {Array} array The array to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {number} Returns the sum.
+     */
+    function arraySum(array, iteratee) {
+      var length = array.length,
+          result = 0;
+
+      while (length--) {
+        result += +iteratee(array[length]) || 0;
+      }
+      return result;
+    }
+
+    /**
+     * Used by `_.defaults` to customize its `_.assign` use.
+     *
+     * @private
+     * @param {*} objectValue The destination object property value.
+     * @param {*} sourceValue The source object property value.
+     * @returns {*} Returns the value to assign to the destination object.
+     */
+    function assignDefaults(objectValue, sourceValue) {
+      return objectValue === undefined ? sourceValue : objectValue;
+    }
+
+    /**
+     * Used by `_.template` to customize its `_.assign` use.
+     *
+     * **Note:** This function is like `assignDefaults` except that it ignores
+     * inherited property values when checking if a property is `undefined`.
+     *
+     * @private
+     * @param {*} objectValue The destination object property value.
+     * @param {*} sourceValue The source object property value.
+     * @param {string} key The key associated with the object and source values.
+     * @param {Object} object The destination object.
+     * @returns {*} Returns the value to assign to the destination object.
+     */
+    function assignOwnDefaults(objectValue, sourceValue, key, object) {
+      return (objectValue === undefined || !hasOwnProperty.call(object, key))
+        ? sourceValue
+        : objectValue;
+    }
+
+    /**
+     * A specialized version of `_.assign` for customizing assigned values without
+     * support for argument juggling, multiple sources, and `this` binding `customizer`
+     * functions.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {Function} customizer The function to customize assigned values.
+     * @returns {Object} Returns `object`.
+     */
+    function assignWith(object, source, customizer) {
+      var index = -1,
+          props = keys(source),
+          length = props.length;
+
+      while (++index < length) {
+        var key = props[index],
+            value = object[key],
+            result = customizer(value, source[key], key, object, source);
+
+        if ((result === result ? (result !== value) : (value === value)) ||
+            (value === undefined && !(key in object))) {
+          object[key] = result;
+        }
+      }
+      return object;
+    }
+
+    /**
+     * The base implementation of `_.assign` without support for argument juggling,
+     * multiple sources, and `customizer` functions.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @returns {Object} Returns `object`.
+     */
+    function baseAssign(object, source) {
+      return source == null
+        ? object
+        : baseCopy(source, keys(source), object);
+    }
+
+    /**
+     * The base implementation of `_.at` without support for string collections
+     * and individual key arguments.
+     *
+     * @private
+     * @param {Array|Object} collection The collection to iterate over.
+     * @param {number[]|string[]} props The property names or indexes of elements to pick.
+     * @returns {Array} Returns the new array of picked elements.
+     */
+    function baseAt(collection, props) {
+      var index = -1,
+          isNil = collection == null,
+          isArr = !isNil && isArrayLike(collection),
+          length = isArr ? collection.length : 0,
+          propsLength = props.length,
+          result = Array(propsLength);
+
+      while(++index < propsLength) {
+        var key = props[index];
+        if (isArr) {
+          result[index] = isIndex(key, length) ? collection[key] : undefined;
+        } else {
+          result[index] = isNil ? undefined : collection[key];
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Copies properties of `source` to `object`.
+     *
+     * @private
+     * @param {Object} source The object to copy properties from.
+     * @param {Array} props The property names to copy.
+     * @param {Object} [object={}] The object to copy properties to.
+     * @returns {Object} Returns `object`.
+     */
+    function baseCopy(source, props, object) {
+      object || (object = {});
+
+      var index = -1,
+          length = props.length;
+
+      while (++index < length) {
+        var key = props[index];
+        object[key] = source[key];
+      }
+      return object;
+    }
+
+    /**
+     * The base implementation of `_.callback` which supports specifying the
+     * number of arguments to provide to `func`.
+     *
+     * @private
+     * @param {*} [func=_.identity] The value to convert to a callback.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {number} [argCount] The number of arguments to provide to `func`.
+     * @returns {Function} Returns the callback.
+     */
+    function baseCallback(func, thisArg, argCount) {
+      var type = typeof func;
+      if (type == 'function') {
+        return thisArg === undefined
+          ? func
+          : bindCallback(func, thisArg, argCount);
+      }
+      if (func == null) {
+        return identity;
+      }
+      if (type == 'object') {
+        return baseMatches(func);
+      }
+      return thisArg === undefined
+        ? property(func)
+        : baseMatchesProperty(func, thisArg);
+    }
+
+    /**
+     * The base implementation of `_.clone` without support for argument juggling
+     * and `this` binding `customizer` functions.
+     *
+     * @private
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @param {Function} [customizer] The function to customize cloning values.
+     * @param {string} [key] The key of `value`.
+     * @param {Object} [object] The object `value` belongs to.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates clones with source counterparts.
+     * @returns {*} Returns the cloned value.
+     */
+    function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
+      var result;
+      if (customizer) {
+        result = object ? customizer(value, key, object) : customizer(value);
+      }
+      if (result !== undefined) {
+        return result;
+      }
+      if (!isObject(value)) {
+        return value;
+      }
+      var isArr = isArray(value);
+      if (isArr) {
+        result = initCloneArray(value);
+        if (!isDeep) {
+          return arrayCopy(value, result);
+        }
+      } else {
+        var tag = objToString.call(value),
+            isFunc = tag == funcTag;
+
+        if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+          result = initCloneObject(isFunc ? {} : value);
+          if (!isDeep) {
+            return baseAssign(result, value);
+          }
+        } else {
+          return cloneableTags[tag]
+            ? initCloneByTag(value, tag, isDeep)
+            : (object ? value : {});
+        }
+      }
+      // Check for circular references and return its corresponding clone.
+      stackA || (stackA = []);
+      stackB || (stackB = []);
+
+      var length = stackA.length;
+      while (length--) {
+        if (stackA[length] == value) {
+          return stackB[length];
+        }
+      }
+      // Add the source value to the stack of traversed objects and associate it with its clone.
+      stackA.push(value);
+      stackB.push(result);
+
+      // Recursively populate clone (susceptible to call stack limits).
+      (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
+        result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.create` without support for assigning
+     * properties to the created object.
+     *
+     * @private
+     * @param {Object} prototype The object to inherit from.
+     * @returns {Object} Returns the new object.
+     */
+    var baseCreate = (function() {
+      function object() {}
+      return function(prototype) {
+        if (isObject(prototype)) {
+          object.prototype = prototype;
+          var result = new object;
+          object.prototype = undefined;
+        }
+        return result || {};
+      };
+    }());
+
+    /**
+     * The base implementation of `_.delay` and `_.defer` which accepts an index
+     * of where to slice the arguments to provide to `func`.
+     *
+     * @private
+     * @param {Function} func The function to delay.
+     * @param {number} wait The number of milliseconds to delay invocation.
+     * @param {Object} args The arguments provide to `func`.
+     * @returns {number} Returns the timer id.
+     */
+    function baseDelay(func, wait, args) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      return setTimeout(function() { func.apply(undefined, args); }, wait);
+    }
+
+    /**
+     * The base implementation of `_.difference` which accepts a single array
+     * of values to exclude.
+     *
+     * @private
+     * @param {Array} array The array to inspect.
+     * @param {Array} values The values to exclude.
+     * @returns {Array} Returns the new array of filtered values.
+     */
+    function baseDifference(array, values) {
+      var length = array ? array.length : 0,
+          result = [];
+
+      if (!length) {
+        return result;
+      }
+      var index = -1,
+          indexOf = getIndexOf(),
+          isCommon = indexOf === baseIndexOf,
+          cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null,
+          valuesLength = values.length;
+
+      if (cache) {
+        indexOf = cacheIndexOf;
+        isCommon = false;
+        values = cache;
+      }
+      outer:
+      while (++index < length) {
+        var value = array[index];
+
+        if (isCommon && value === value) {
+          var valuesIndex = valuesLength;
+          while (valuesIndex--) {
+            if (values[valuesIndex] === value) {
+              continue outer;
+            }
+          }
+          result.push(value);
+        }
+        else if (indexOf(values, value, 0) < 0) {
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.forEach` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array|Object|string} Returns `collection`.
+     */
+    var baseEach = createBaseEach(baseForOwn);
+
+    /**
+     * The base implementation of `_.forEachRight` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array|Object|string} Returns `collection`.
+     */
+    var baseEachRight = createBaseEach(baseForOwnRight, true);
+
+    /**
+     * The base implementation of `_.every` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {boolean} Returns `true` if all elements pass the predicate check,
+     *  else `false`
+     */
+    function baseEvery(collection, predicate) {
+      var result = true;
+      baseEach(collection, function(value, index, collection) {
+        result = !!predicate(value, index, collection);
+        return result;
+      });
+      return result;
+    }
+
+    /**
+     * Gets the extremum value of `collection` invoking `iteratee` for each value
+     * in `collection` to generate the criterion by which the value is ranked.
+     * The `iteratee` is invoked with three arguments: (value, index|key, collection).
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {Function} comparator The function used to compare values.
+     * @param {*} exValue The initial extremum value.
+     * @returns {*} Returns the extremum value.
+     */
+    function baseExtremum(collection, iteratee, comparator, exValue) {
+      var computed = exValue,
+          result = computed;
+
+      baseEach(collection, function(value, index, collection) {
+        var current = +iteratee(value, index, collection);
+        if (comparator(current, computed) || (current === exValue && current === result)) {
+          computed = current;
+          result = value;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.fill` without an iteratee call guard.
+     *
+     * @private
+     * @param {Array} array The array to fill.
+     * @param {*} value The value to fill `array` with.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns `array`.
+     */
+    function baseFill(array, value, start, end) {
+      var length = array.length;
+
+      start = start == null ? 0 : (+start || 0);
+      if (start < 0) {
+        start = -start > length ? 0 : (length + start);
+      }
+      end = (end === undefined || end > length) ? length : (+end || 0);
+      if (end < 0) {
+        end += length;
+      }
+      length = start > end ? 0 : (end >>> 0);
+      start >>>= 0;
+
+      while (start < length) {
+        array[start++] = value;
+      }
+      return array;
+    }
+
+    /**
+     * The base implementation of `_.filter` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {Array} Returns the new filtered array.
+     */
+    function baseFilter(collection, predicate) {
+      var result = [];
+      baseEach(collection, function(value, index, collection) {
+        if (predicate(value, index, collection)) {
+          result.push(value);
+        }
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
+     * without support for callback shorthands and `this` binding, which iterates
+     * over `collection` using the provided `eachFunc`.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {Function} predicate The function invoked per iteration.
+     * @param {Function} eachFunc The function to iterate over `collection`.
+     * @param {boolean} [retKey] Specify returning the key of the found element
+     *  instead of the element itself.
+     * @returns {*} Returns the found element or its key, else `undefined`.
+     */
+    function baseFind(collection, predicate, eachFunc, retKey) {
+      var result;
+      eachFunc(collection, function(value, key, collection) {
+        if (predicate(value, key, collection)) {
+          result = retKey ? key : value;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.flatten` with added support for restricting
+     * flattening and specifying the start index.
+     *
+     * @private
+     * @param {Array} array The array to flatten.
+     * @param {boolean} [isDeep] Specify a deep flatten.
+     * @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
+     * @param {Array} [result=[]] The initial result value.
+     * @returns {Array} Returns the new flattened array.
+     */
+    function baseFlatten(array, isDeep, isStrict, result) {
+      result || (result = []);
+
+      var index = -1,
+          length = array.length;
+
+      while (++index < length) {
+        var value = array[index];
+        if (isObjectLike(value) && isArrayLike(value) &&
+            (isStrict || isArray(value) || isArguments(value))) {
+          if (isDeep) {
+            // Recursively flatten arrays (susceptible to call stack limits).
+            baseFlatten(value, isDeep, isStrict, result);
+          } else {
+            arrayPush(result, value);
+          }
+        } else if (!isStrict) {
+          result[result.length] = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `baseForIn` and `baseForOwn` which iterates
+     * over `object` properties returned by `keysFunc` invoking `iteratee` for
+     * each property. Iteratee functions may exit iteration early by explicitly
+     * returning `false`.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {Function} keysFunc The function to get the keys of `object`.
+     * @returns {Object} Returns `object`.
+     */
+    var baseFor = createBaseFor();
+
+    /**
+     * This function is like `baseFor` except that it iterates over properties
+     * in the opposite order.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {Function} keysFunc The function to get the keys of `object`.
+     * @returns {Object} Returns `object`.
+     */
+    var baseForRight = createBaseFor(true);
+
+    /**
+     * The base implementation of `_.forIn` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     */
+    function baseForIn(object, iteratee) {
+      return baseFor(object, iteratee, keysIn);
+    }
+
+    /**
+     * The base implementation of `_.forOwn` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     */
+    function baseForOwn(object, iteratee) {
+      return baseFor(object, iteratee, keys);
+    }
+
+    /**
+     * The base implementation of `_.forOwnRight` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Object} object The object to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Object} Returns `object`.
+     */
+    function baseForOwnRight(object, iteratee) {
+      return baseForRight(object, iteratee, keys);
+    }
+
+    /**
+     * The base implementation of `_.functions` which creates an array of
+     * `object` function property names filtered from those provided.
+     *
+     * @private
+     * @param {Object} object The object to inspect.
+     * @param {Array} props The property names to filter.
+     * @returns {Array} Returns the new array of filtered property names.
+     */
+    function baseFunctions(object, props) {
+      var index = -1,
+          length = props.length,
+          resIndex = -1,
+          result = [];
+
+      while (++index < length) {
+        var key = props[index];
+        if (isFunction(object[key])) {
+          result[++resIndex] = key;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `get` without support for string paths
+     * and default values.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array} path The path of the property to get.
+     * @param {string} [pathKey] The key representation of path.
+     * @returns {*} Returns the resolved value.
+     */
+    function baseGet(object, path, pathKey) {
+      if (object == null) {
+        return;
+      }
+      if (pathKey !== undefined && pathKey in toObject(object)) {
+        path = [pathKey];
+      }
+      var index = 0,
+          length = path.length;
+
+      while (object != null && index < length) {
+        object = object[path[index++]];
+      }
+      return (index && index == length) ? object : undefined;
+    }
+
+    /**
+     * The base implementation of `_.isEqual` without support for `this` binding
+     * `customizer` functions.
+     *
+     * @private
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @param {Function} [customizer] The function to customize comparing values.
+     * @param {boolean} [isLoose] Specify performing partial comparisons.
+     * @param {Array} [stackA] Tracks traversed `value` objects.
+     * @param {Array} [stackB] Tracks traversed `other` objects.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     */
+    function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
+      if (value === other) {
+        return true;
+      }
+      if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
+        return value !== value && other !== other;
+      }
+      return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
+    }
+
+    /**
+     * A specialized version of `baseIsEqual` for arrays and objects which performs
+     * deep comparisons and tracks traversed objects enabling objects with circular
+     * references to be compared.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} [customizer] The function to customize comparing objects.
+     * @param {boolean} [isLoose] Specify performing partial comparisons.
+     * @param {Array} [stackA=[]] Tracks traversed `value` objects.
+     * @param {Array} [stackB=[]] Tracks traversed `other` objects.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
+      var objIsArr = isArray(object),
+          othIsArr = isArray(other),
+          objTag = arrayTag,
+          othTag = arrayTag;
+
+      if (!objIsArr) {
+        objTag = objToString.call(object);
+        if (objTag == argsTag) {
+          objTag = objectTag;
+        } else if (objTag != objectTag) {
+          objIsArr = isTypedArray(object);
+        }
+      }
+      if (!othIsArr) {
+        othTag = objToString.call(other);
+        if (othTag == argsTag) {
+          othTag = objectTag;
+        } else if (othTag != objectTag) {
+          othIsArr = isTypedArray(other);
+        }
+      }
+      var objIsObj = objTag == objectTag,
+          othIsObj = othTag == objectTag,
+          isSameTag = objTag == othTag;
+
+      if (isSameTag && !(objIsArr || objIsObj)) {
+        return equalByTag(object, other, objTag);
+      }
+      if (!isLoose) {
+        var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+            othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+        if (objIsWrapped || othIsWrapped) {
+          return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
+        }
+      }
+      if (!isSameTag) {
+        return false;
+      }
+      // Assume cyclic values are equal.
+      // For more information on detecting circular references see https://es5.github.io/#JO.
+      stackA || (stackA = []);
+      stackB || (stackB = []);
+
+      var length = stackA.length;
+      while (length--) {
+        if (stackA[length] == object) {
+          return stackB[length] == other;
+        }
+      }
+      // Add `object` and `other` to the stack of traversed objects.
+      stackA.push(object);
+      stackB.push(other);
+
+      var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
+
+      stackA.pop();
+      stackB.pop();
+
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.isMatch` without support for callback
+     * shorthands and `this` binding.
+     *
+     * @private
+     * @param {Object} object The object to inspect.
+     * @param {Array} matchData The propery names, values, and compare flags to match.
+     * @param {Function} [customizer] The function to customize comparing objects.
+     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+     */
+    function baseIsMatch(object, matchData, customizer) {
+      var index = matchData.length,
+          length = index,
+          noCustomizer = !customizer;
+
+      if (object == null) {
+        return !length;
+      }
+      object = toObject(object);
+      while (index--) {
+        var data = matchData[index];
+        if ((noCustomizer && data[2])
+              ? data[1] !== object[data[0]]
+              : !(data[0] in object)
+            ) {
+          return false;
+        }
+      }
+      while (++index < length) {
+        data = matchData[index];
+        var key = data[0],
+            objValue = object[key],
+            srcValue = data[1];
+
+        if (noCustomizer && data[2]) {
+          if (objValue === undefined && !(key in object)) {
+            return false;
+          }
+        } else {
+          var result = customizer ? customizer(objValue, srcValue, key) : undefined;
+          if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) {
+            return false;
+          }
+        }
+      }
+      return true;
+    }
+
+    /**
+     * The base implementation of `_.map` without support for callback shorthands
+     * and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {Array} Returns the new mapped array.
+     */
+    function baseMap(collection, iteratee) {
+      var index = -1,
+          result = isArrayLike(collection) ? Array(collection.length) : [];
+
+      baseEach(collection, function(value, key, collection) {
+        result[++index] = iteratee(value, key, collection);
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.matches` which does not clone `source`.
+     *
+     * @private
+     * @param {Object} source The object of property values to match.
+     * @returns {Function} Returns the new function.
+     */
+    function baseMatches(source) {
+      var matchData = getMatchData(source);
+      if (matchData.length == 1 && matchData[0][2]) {
+        var key = matchData[0][0],
+            value = matchData[0][1];
+
+        return function(object) {
+          if (object == null) {
+            return false;
+          }
+          return object[key] === value && (value !== undefined || (key in toObject(object)));
+        };
+      }
+      return function(object) {
+        return baseIsMatch(object, matchData);
+      };
+    }
+
+    /**
+     * The base implementation of `_.matchesProperty` which does not clone `srcValue`.
+     *
+     * @private
+     * @param {string} path The path of the property to get.
+     * @param {*} srcValue The value to compare.
+     * @returns {Function} Returns the new function.
+     */
+    function baseMatchesProperty(path, srcValue) {
+      var isArr = isArray(path),
+          isCommon = isKey(path) && isStrictComparable(srcValue),
+          pathKey = (path + '');
+
+      path = toPath(path);
+      return function(object) {
+        if (object == null) {
+          return false;
+        }
+        var key = pathKey;
+        object = toObject(object);
+        if ((isArr || !isCommon) && !(key in object)) {
+          object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
+          if (object == null) {
+            return false;
+          }
+          key = last(path);
+          object = toObject(object);
+        }
+        return object[key] === srcValue
+          ? (srcValue !== undefined || (key in object))
+          : baseIsEqual(srcValue, object[key], undefined, true);
+      };
+    }
+
+    /**
+     * The base implementation of `_.merge` without support for argument juggling,
+     * multiple sources, and `this` binding `customizer` functions.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {Function} [customizer] The function to customize merged values.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates values with source counterparts.
+     * @returns {Object} Returns `object`.
+     */
+    function baseMerge(object, source, customizer, stackA, stackB) {
+      if (!isObject(object)) {
+        return object;
+      }
+      var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
+          props = isSrcArr ? undefined : keys(source);
+
+      arrayEach(props || source, function(srcValue, key) {
+        if (props) {
+          key = srcValue;
+          srcValue = source[key];
+        }
+        if (isObjectLike(srcValue)) {
+          stackA || (stackA = []);
+          stackB || (stackB = []);
+          baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
+        }
+        else {
+          var value = object[key],
+              result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
+              isCommon = result === undefined;
+
+          if (isCommon) {
+            result = srcValue;
+          }
+          if ((result !== undefined || (isSrcArr && !(key in object))) &&
+              (isCommon || (result === result ? (result !== value) : (value === value)))) {
+            object[key] = result;
+          }
+        }
+      });
+      return object;
+    }
+
+    /**
+     * A specialized version of `baseMerge` for arrays and objects which performs
+     * deep merges and tracks traversed objects enabling objects with circular
+     * references to be merged.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {string} key The key of the value to merge.
+     * @param {Function} mergeFunc The function to merge values.
+     * @param {Function} [customizer] The function to customize merged values.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates values with source counterparts.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
+      var length = stackA.length,
+          srcValue = source[key];
+
+      while (length--) {
+        if (stackA[length] == srcValue) {
+          object[key] = stackB[length];
+          return;
+        }
+      }
+      var value = object[key],
+          result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
+          isCommon = result === undefined;
+
+      if (isCommon) {
+        result = srcValue;
+        if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
+          result = isArray(value)
+            ? value
+            : (isArrayLike(value) ? arrayCopy(value) : []);
+        }
+        else if (isPlainObject(srcValue) || isArguments(srcValue)) {
+          result = isArguments(value)
+            ? toPlainObject(value)
+            : (isPlainObject(value) ? value : {});
+        }
+        else {
+          isCommon = false;
+        }
+      }
+      // Add the source value to the stack of traversed objects and associate
+      // it with its merged value.
+      stackA.push(srcValue);
+      stackB.push(result);
+
+      if (isCommon) {
+        // Recursively merge objects and arrays (susceptible to call stack limits).
+        object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
+      } else if (result === result ? (result !== value) : (value === value)) {
+        object[key] = result;
+      }
+    }
+
+    /**
+     * The base implementation of `_.property` without support for deep paths.
+     *
+     * @private
+     * @param {string} key The key of the property to get.
+     * @returns {Function} Returns the new function.
+     */
+    function baseProperty(key) {
+      return function(object) {
+        return object == null ? undefined : object[key];
+      };
+    }
+
+    /**
+     * A specialized version of `baseProperty` which supports deep paths.
+     *
+     * @private
+     * @param {Array|string} path The path of the property to get.
+     * @returns {Function} Returns the new function.
+     */
+    function basePropertyDeep(path) {
+      var pathKey = (path + '');
+      path = toPath(path);
+      return function(object) {
+        return baseGet(object, path, pathKey);
+      };
+    }
+
+    /**
+     * The base implementation of `_.pullAt` without support for individual
+     * index arguments and capturing the removed elements.
+     *
+     * @private
+     * @param {Array} array The array to modify.
+     * @param {number[]} indexes The indexes of elements to remove.
+     * @returns {Array} Returns `array`.
+     */
+    function basePullAt(array, indexes) {
+      var length = array ? indexes.length : 0;
+      while (length--) {
+        var index = indexes[length];
+        if (index != previous && isIndex(index)) {
+          var previous = index;
+          splice.call(array, index, 1);
+        }
+      }
+      return array;
+    }
+
+    /**
+     * The base implementation of `_.random` without support for argument juggling
+     * and returning floating-point numbers.
+     *
+     * @private
+     * @param {number} min The minimum possible value.
+     * @param {number} max The maximum possible value.
+     * @returns {number} Returns the random number.
+     */
+    function baseRandom(min, max) {
+      return min + nativeFloor(nativeRandom() * (max - min + 1));
+    }
+
+    /**
+     * The base implementation of `_.reduce` and `_.reduceRight` without support
+     * for callback shorthands and `this` binding, which iterates over `collection`
+     * using the provided `eachFunc`.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {*} accumulator The initial value.
+     * @param {boolean} initFromCollection Specify using the first or last element
+     *  of `collection` as the initial value.
+     * @param {Function} eachFunc The function to iterate over `collection`.
+     * @returns {*} Returns the accumulated value.
+     */
+    function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) {
+      eachFunc(collection, function(value, index, collection) {
+        accumulator = initFromCollection
+          ? (initFromCollection = false, value)
+          : iteratee(accumulator, value, index, collection);
+      });
+      return accumulator;
+    }
+
+    /**
+     * The base implementation of `setData` without support for hot loop detection.
+     *
+     * @private
+     * @param {Function} func The function to associate metadata with.
+     * @param {*} data The metadata.
+     * @returns {Function} Returns `func`.
+     */
+    var baseSetData = !metaMap ? identity : function(func, data) {
+      metaMap.set(func, data);
+      return func;
+    };
+
+    /**
+     * The base implementation of `_.slice` without an iteratee call guard.
+     *
+     * @private
+     * @param {Array} array The array to slice.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns the slice of `array`.
+     */
+    function baseSlice(array, start, end) {
+      var index = -1,
+          length = array.length;
+
+      start = start == null ? 0 : (+start || 0);
+      if (start < 0) {
+        start = -start > length ? 0 : (length + start);
+      }
+      end = (end === undefined || end > length) ? length : (+end || 0);
+      if (end < 0) {
+        end += length;
+      }
+      length = start > end ? 0 : ((end - start) >>> 0);
+      start >>>= 0;
+
+      var result = Array(length);
+      while (++index < length) {
+        result[index] = array[index + start];
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.some` without support for callback shorthands
+     * and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {boolean} Returns `true` if any element passes the predicate check,
+     *  else `false`.
+     */
+    function baseSome(collection, predicate) {
+      var result;
+
+      baseEach(collection, function(value, index, collection) {
+        result = predicate(value, index, collection);
+        return !result;
+      });
+      return !!result;
+    }
+
+    /**
+     * The base implementation of `_.sortBy` which uses `comparer` to define
+     * the sort order of `array` and replaces criteria objects with their
+     * corresponding values.
+     *
+     * @private
+     * @param {Array} array The array to sort.
+     * @param {Function} comparer The function to define sort order.
+     * @returns {Array} Returns `array`.
+     */
+    function baseSortBy(array, comparer) {
+      var length = array.length;
+
+      array.sort(comparer);
+      while (length--) {
+        array[length] = array[length].value;
+      }
+      return array;
+    }
+
+    /**
+     * The base implementation of `_.sortByOrder` without param guards.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
+     * @param {boolean[]} orders The sort orders of `iteratees`.
+     * @returns {Array} Returns the new sorted array.
+     */
+    function baseSortByOrder(collection, iteratees, orders) {
+      var callback = getCallback(),
+          index = -1;
+
+      iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); });
+
+      var result = baseMap(collection, function(value) {
+        var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); });
+        return { 'criteria': criteria, 'index': ++index, 'value': value };
+      });
+
+      return baseSortBy(result, function(object, other) {
+        return compareMultiple(object, other, orders);
+      });
+    }
+
+    /**
+     * The base implementation of `_.sum` without support for callback shorthands
+     * and `this` binding.
+     *
+     * @private
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @returns {number} Returns the sum.
+     */
+    function baseSum(collection, iteratee) {
+      var result = 0;
+      baseEach(collection, function(value, index, collection) {
+        result += +iteratee(value, index, collection) || 0;
+      });
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.uniq` without support for callback shorthands
+     * and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to inspect.
+     * @param {Function} [iteratee] The function invoked per iteration.
+     * @returns {Array} Returns the new duplicate free array.
+     */
+    function baseUniq(array, iteratee) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = array.length,
+          isCommon = indexOf === baseIndexOf,
+          isLarge = isCommon && length >= LARGE_ARRAY_SIZE,
+          seen = isLarge ? createCache() : null,
+          result = [];
+
+      if (seen) {
+        indexOf = cacheIndexOf;
+        isCommon = false;
+      } else {
+        isLarge = false;
+        seen = iteratee ? [] : result;
+      }
+      outer:
+      while (++index < length) {
+        var value = array[index],
+            computed = iteratee ? iteratee(value, index, array) : value;
+
+        if (isCommon && value === value) {
+          var seenIndex = seen.length;
+          while (seenIndex--) {
+            if (seen[seenIndex] === computed) {
+              continue outer;
+            }
+          }
+          if (iteratee) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+        else if (indexOf(seen, computed, 0) < 0) {
+          if (iteratee || isLarge) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.values` and `_.valuesIn` which creates an
+     * array of `object` property values corresponding to the property names
+     * of `props`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array} props The property names to get values for.
+     * @returns {Object} Returns the array of property values.
+     */
+    function baseValues(object, props) {
+      var index = -1,
+          length = props.length,
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = object[props[index]];
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.dropRightWhile`, `_.dropWhile`, `_.takeRightWhile`,
+     * and `_.takeWhile` without support for callback shorthands and `this` binding.
+     *
+     * @private
+     * @param {Array} array The array to query.
+     * @param {Function} predicate The function invoked per iteration.
+     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Array} Returns the slice of `array`.
+     */
+    function baseWhile(array, predicate, isDrop, fromRight) {
+      var length = array.length,
+          index = fromRight ? length : -1;
+
+      while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {}
+      return isDrop
+        ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
+        : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
+    }
+
+    /**
+     * The base implementation of `wrapperValue` which returns the result of
+     * performing a sequence of actions on the unwrapped `value`, where each
+     * successive action is supplied the return value of the previous.
+     *
+     * @private
+     * @param {*} value The unwrapped value.
+     * @param {Array} actions Actions to peform to resolve the unwrapped value.
+     * @returns {*} Returns the resolved value.
+     */
+    function baseWrapperValue(value, actions) {
+      var result = value;
+      if (result instanceof LazyWrapper) {
+        result = result.value();
+      }
+      var index = -1,
+          length = actions.length;
+
+      while (++index < length) {
+        var action = actions[index];
+        result = action.func.apply(action.thisArg, arrayPush([result], action.args));
+      }
+      return result;
+    }
+
+    /**
+     * Performs a binary search of `array` to determine the index at which `value`
+     * should be inserted into `array` in order to maintain its sort order.
+     *
+     * @private
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {boolean} [retHighest] Specify returning the highest qualified index.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     */
+    function binaryIndex(array, value, retHighest) {
+      var low = 0,
+          high = array ? array.length : low;
+
+      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
+        while (low < high) {
+          var mid = (low + high) >>> 1,
+              computed = array[mid];
+
+          if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {
+            low = mid + 1;
+          } else {
+            high = mid;
+          }
+        }
+        return high;
+      }
+      return binaryIndexBy(array, value, identity, retHighest);
+    }
+
+    /**
+     * This function is like `binaryIndex` except that it invokes `iteratee` for
+     * `value` and each element of `array` to compute their sort ranking. The
+     * iteratee is invoked with one argument; (value).
+     *
+     * @private
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function} iteratee The function invoked per iteration.
+     * @param {boolean} [retHighest] Specify returning the highest qualified index.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     */
+    function binaryIndexBy(array, value, iteratee, retHighest) {
+      value = iteratee(value);
+
+      var low = 0,
+          high = array ? array.length : 0,
+          valIsNaN = value !== value,
+          valIsNull = value === null,
+          valIsUndef = value === undefined;
+
+      while (low < high) {
+        var mid = nativeFloor((low + high) / 2),
+            computed = iteratee(array[mid]),
+            isDef = computed !== undefined,
+            isReflexive = computed === computed;
+
+        if (valIsNaN) {
+          var setLow = isReflexive || retHighest;
+        } else if (valIsNull) {
+          setLow = isReflexive && isDef && (retHighest || computed != null);
+        } else if (valIsUndef) {
+          setLow = isReflexive && (retHighest || isDef);
+        } else if (computed == null) {
+          setLow = false;
+        } else {
+          setLow = retHighest ? (computed <= value) : (computed < value);
+        }
+        if (setLow) {
+          low = mid + 1;
+        } else {
+          high = mid;
+        }
+      }
+      return nativeMin(high, MAX_ARRAY_INDEX);
+    }
+
+    /**
+     * A specialized version of `baseCallback` which only supports `this` binding
+     * and specifying the number of arguments to provide to `func`.
+     *
+     * @private
+     * @param {Function} func The function to bind.
+     * @param {*} thisArg The `this` binding of `func`.
+     * @param {number} [argCount] The number of arguments to provide to `func`.
+     * @returns {Function} Returns the callback.
+     */
+    function bindCallback(func, thisArg, argCount) {
+      if (typeof func != 'function') {
+        return identity;
+      }
+      if (thisArg === undefined) {
+        return func;
+      }
+      switch (argCount) {
+        case 1: return function(value) {
+          return func.call(thisArg, value);
+        };
+        case 3: return function(value, index, collection) {
+          return func.call(thisArg, value, index, collection);
+        };
+        case 4: return function(accumulator, value, index, collection) {
+          return func.call(thisArg, accumulator, value, index, collection);
+        };
+        case 5: return function(value, other, key, object, source) {
+          return func.call(thisArg, value, other, key, object, source);
+        };
+      }
+      return function() {
+        return func.apply(thisArg, arguments);
+      };
+    }
+
+    /**
+     * Creates a clone of the given array buffer.
+     *
+     * @private
+     * @param {ArrayBuffer} buffer The array buffer to clone.
+     * @returns {ArrayBuffer} Returns the cloned array buffer.
+     */
+    function bufferClone(buffer) {
+      var result = new ArrayBuffer(buffer.byteLength),
+          view = new Uint8Array(result);
+
+      view.set(new Uint8Array(buffer));
+      return result;
+    }
+
+    /**
+     * Creates an array that is the composition of partially applied arguments,
+     * placeholders, and provided arguments into a single array of arguments.
+     *
+     * @private
+     * @param {Array|Object} args The provided arguments.
+     * @param {Array} partials The arguments to prepend to those provided.
+     * @param {Array} holders The `partials` placeholder indexes.
+     * @returns {Array} Returns the new array of composed arguments.
+     */
+    function composeArgs(args, partials, holders) {
+      var holdersLength = holders.length,
+          argsIndex = -1,
+          argsLength = nativeMax(args.length - holdersLength, 0),
+          leftIndex = -1,
+          leftLength = partials.length,
+          result = Array(leftLength + argsLength);
+
+      while (++leftIndex < leftLength) {
+        result[leftIndex] = partials[leftIndex];
+      }
+      while (++argsIndex < holdersLength) {
+        result[holders[argsIndex]] = args[argsIndex];
+      }
+      while (argsLength--) {
+        result[leftIndex++] = args[argsIndex++];
+      }
+      return result;
+    }
+
+    /**
+     * This function is like `composeArgs` except that the arguments composition
+     * is tailored for `_.partialRight`.
+     *
+     * @private
+     * @param {Array|Object} args The provided arguments.
+     * @param {Array} partials The arguments to append to those provided.
+     * @param {Array} holders The `partials` placeholder indexes.
+     * @returns {Array} Returns the new array of composed arguments.
+     */
+    function composeArgsRight(args, partials, holders) {
+      var holdersIndex = -1,
+          holdersLength = holders.length,
+          argsIndex = -1,
+          argsLength = nativeMax(args.length - holdersLength, 0),
+          rightIndex = -1,
+          rightLength = partials.length,
+          result = Array(argsLength + rightLength);
+
+      while (++argsIndex < argsLength) {
+        result[argsIndex] = args[argsIndex];
+      }
+      var offset = argsIndex;
+      while (++rightIndex < rightLength) {
+        result[offset + rightIndex] = partials[rightIndex];
+      }
+      while (++holdersIndex < holdersLength) {
+        result[offset + holders[holdersIndex]] = args[argsIndex++];
+      }
+      return result;
+    }
+
+    /**
+     * Creates a `_.countBy`, `_.groupBy`, `_.indexBy`, or `_.partition` function.
+     *
+     * @private
+     * @param {Function} setter The function to set keys and values of the accumulator object.
+     * @param {Function} [initializer] The function to initialize the accumulator object.
+     * @returns {Function} Returns the new aggregator function.
+     */
+    function createAggregator(setter, initializer) {
+      return function(collection, iteratee, thisArg) {
+        var result = initializer ? initializer() : {};
+        iteratee = getCallback(iteratee, thisArg, 3);
+
+        if (isArray(collection)) {
+          var index = -1,
+              length = collection.length;
+
+          while (++index < length) {
+            var value = collection[index];
+            setter(result, value, iteratee(value, index, collection), collection);
+          }
+        } else {
+          baseEach(collection, function(value, key, collection) {
+            setter(result, value, iteratee(value, key, collection), collection);
+          });
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a `_.assign`, `_.defaults`, or `_.merge` function.
+     *
+     * @private
+     * @param {Function} assigner The function to assign values.
+     * @returns {Function} Returns the new assigner function.
+     */
+    function createAssigner(assigner) {
+      return restParam(function(object, sources) {
+        var index = -1,
+            length = object == null ? 0 : sources.length,
+            customizer = length > 2 ? sources[length - 2] : undefined,
+            guard = length > 2 ? sources[2] : undefined,
+            thisArg = length > 1 ? sources[length - 1] : undefined;
+
+        if (typeof customizer == 'function') {
+          customizer = bindCallback(customizer, thisArg, 5);
+          length -= 2;
+        } else {
+          customizer = typeof thisArg == 'function' ? thisArg : undefined;
+          length -= (customizer ? 1 : 0);
+        }
+        if (guard && isIterateeCall(sources[0], sources[1], guard)) {
+          customizer = length < 3 ? undefined : customizer;
+          length = 1;
+        }
+        while (++index < length) {
+          var source = sources[index];
+          if (source) {
+            assigner(object, source, customizer);
+          }
+        }
+        return object;
+      });
+    }
+
+    /**
+     * Creates a `baseEach` or `baseEachRight` function.
+     *
+     * @private
+     * @param {Function} eachFunc The function to iterate over a collection.
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new base function.
+     */
+    function createBaseEach(eachFunc, fromRight) {
+      return function(collection, iteratee) {
+        var length = collection ? getLength(collection) : 0;
+        if (!isLength(length)) {
+          return eachFunc(collection, iteratee);
+        }
+        var index = fromRight ? length : -1,
+            iterable = toObject(collection);
+
+        while ((fromRight ? index-- : ++index < length)) {
+          if (iteratee(iterable[index], index, iterable) === false) {
+            break;
+          }
+        }
+        return collection;
+      };
+    }
+
+    /**
+     * Creates a base function for `_.forIn` or `_.forInRight`.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new base function.
+     */
+    function createBaseFor(fromRight) {
+      return function(object, iteratee, keysFunc) {
+        var iterable = toObject(object),
+            props = keysFunc(object),
+            length = props.length,
+            index = fromRight ? length : -1;
+
+        while ((fromRight ? index-- : ++index < length)) {
+          var key = props[index];
+          if (iteratee(iterable[key], key, iterable) === false) {
+            break;
+          }
+        }
+        return object;
+      };
+    }
+
+    /**
+     * Creates a function that wraps `func` and invokes it with the `this`
+     * binding of `thisArg`.
+     *
+     * @private
+     * @param {Function} func The function to bind.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @returns {Function} Returns the new bound function.
+     */
+    function createBindWrapper(func, thisArg) {
+      var Ctor = createCtorWrapper(func);
+
+      function wrapper() {
+        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+        return fn.apply(thisArg, arguments);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates a `Set` cache object to optimize linear searches of large arrays.
+     *
+     * @private
+     * @param {Array} [values] The values to cache.
+     * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`.
+     */
+    function createCache(values) {
+      return (nativeCreate && Set) ? new SetCache(values) : null;
+    }
+
+    /**
+     * Creates a function that produces compound words out of the words in a
+     * given string.
+     *
+     * @private
+     * @param {Function} callback The function to combine each word.
+     * @returns {Function} Returns the new compounder function.
+     */
+    function createCompounder(callback) {
+      return function(string) {
+        var index = -1,
+            array = words(deburr(string)),
+            length = array.length,
+            result = '';
+
+        while (++index < length) {
+          result = callback(result, array[index], index);
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that produces an instance of `Ctor` regardless of
+     * whether it was invoked as part of a `new` expression or by `call` or `apply`.
+     *
+     * @private
+     * @param {Function} Ctor The constructor to wrap.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createCtorWrapper(Ctor) {
+      return function() {
+        // Use a `switch` statement to work with class constructors.
+        // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
+        // for more details.
+        var args = arguments;
+        switch (args.length) {
+          case 0: return new Ctor;
+          case 1: return new Ctor(args[0]);
+          case 2: return new Ctor(args[0], args[1]);
+          case 3: return new Ctor(args[0], args[1], args[2]);
+          case 4: return new Ctor(args[0], args[1], args[2], args[3]);
+          case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
+          case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
+          case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+        }
+        var thisBinding = baseCreate(Ctor.prototype),
+            result = Ctor.apply(thisBinding, args);
+
+        // Mimic the constructor's `return` behavior.
+        // See https://es5.github.io/#x13.2.2 for more details.
+        return isObject(result) ? result : thisBinding;
+      };
+    }
+
+    /**
+     * Creates a `_.curry` or `_.curryRight` function.
+     *
+     * @private
+     * @param {boolean} flag The curry bit flag.
+     * @returns {Function} Returns the new curry function.
+     */
+    function createCurry(flag) {
+      function curryFunc(func, arity, guard) {
+        if (guard && isIterateeCall(func, arity, guard)) {
+          arity = undefined;
+        }
+        var result = createWrapper(func, flag, undefined, undefined, undefined, undefined, undefined, arity);
+        result.placeholder = curryFunc.placeholder;
+        return result;
+      }
+      return curryFunc;
+    }
+
+    /**
+     * Creates a `_.defaults` or `_.defaultsDeep` function.
+     *
+     * @private
+     * @param {Function} assigner The function to assign values.
+     * @param {Function} customizer The function to customize assigned values.
+     * @returns {Function} Returns the new defaults function.
+     */
+    function createDefaults(assigner, customizer) {
+      return restParam(function(args) {
+        var object = args[0];
+        if (object == null) {
+          return object;
+        }
+        args.push(customizer);
+        return assigner.apply(undefined, args);
+      });
+    }
+
+    /**
+     * Creates a `_.max` or `_.min` function.
+     *
+     * @private
+     * @param {Function} comparator The function used to compare values.
+     * @param {*} exValue The initial extremum value.
+     * @returns {Function} Returns the new extremum function.
+     */
+    function createExtremum(comparator, exValue) {
+      return function(collection, iteratee, thisArg) {
+        if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
+          iteratee = undefined;
+        }
+        iteratee = getCallback(iteratee, thisArg, 3);
+        if (iteratee.length == 1) {
+          collection = isArray(collection) ? collection : toIterable(collection);
+          var result = arrayExtremum(collection, iteratee, comparator, exValue);
+          if (!(collection.length && result === exValue)) {
+            return result;
+          }
+        }
+        return baseExtremum(collection, iteratee, comparator, exValue);
+      };
+    }
+
+    /**
+     * Creates a `_.find` or `_.findLast` function.
+     *
+     * @private
+     * @param {Function} eachFunc The function to iterate over a collection.
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new find function.
+     */
+    function createFind(eachFunc, fromRight) {
+      return function(collection, predicate, thisArg) {
+        predicate = getCallback(predicate, thisArg, 3);
+        if (isArray(collection)) {
+          var index = baseFindIndex(collection, predicate, fromRight);
+          return index > -1 ? collection[index] : undefined;
+        }
+        return baseFind(collection, predicate, eachFunc);
+      };
+    }
+
+    /**
+     * Creates a `_.findIndex` or `_.findLastIndex` function.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new find function.
+     */
+    function createFindIndex(fromRight) {
+      return function(array, predicate, thisArg) {
+        if (!(array && array.length)) {
+          return -1;
+        }
+        predicate = getCallback(predicate, thisArg, 3);
+        return baseFindIndex(array, predicate, fromRight);
+      };
+    }
+
+    /**
+     * Creates a `_.findKey` or `_.findLastKey` function.
+     *
+     * @private
+     * @param {Function} objectFunc The function to iterate over an object.
+     * @returns {Function} Returns the new find function.
+     */
+    function createFindKey(objectFunc) {
+      return function(object, predicate, thisArg) {
+        predicate = getCallback(predicate, thisArg, 3);
+        return baseFind(object, predicate, objectFunc, true);
+      };
+    }
+
+    /**
+     * Creates a `_.flow` or `_.flowRight` function.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify iterating from right to left.
+     * @returns {Function} Returns the new flow function.
+     */
+    function createFlow(fromRight) {
+      return function() {
+        var wrapper,
+            length = arguments.length,
+            index = fromRight ? length : -1,
+            leftIndex = 0,
+            funcs = Array(length);
+
+        while ((fromRight ? index-- : ++index < length)) {
+          var func = funcs[leftIndex++] = arguments[index];
+          if (typeof func != 'function') {
+            throw new TypeError(FUNC_ERROR_TEXT);
+          }
+          if (!wrapper && LodashWrapper.prototype.thru && getFuncName(func) == 'wrapper') {
+            wrapper = new LodashWrapper([], true);
+          }
+        }
+        index = wrapper ? -1 : length;
+        while (++index < length) {
+          func = funcs[index];
+
+          var funcName = getFuncName(func),
+              data = funcName == 'wrapper' ? getData(func) : undefined;
+
+          if (data && isLaziable(data[0]) && data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) && !data[4].length && data[9] == 1) {
+            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
+          } else {
+            wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func);
+          }
+        }
+        return function() {
+          var args = arguments,
+              value = args[0];
+
+          if (wrapper && args.length == 1 && isArray(value) && value.length >= LARGE_ARRAY_SIZE) {
+            return wrapper.plant(value).value();
+          }
+          var index = 0,
+              result = length ? funcs[index].apply(this, args) : value;
+
+          while (++index < length) {
+            result = funcs[index].call(this, result);
+          }
+          return result;
+        };
+      };
+    }
+
+    /**
+     * Creates a function for `_.forEach` or `_.forEachRight`.
+     *
+     * @private
+     * @param {Function} arrayFunc The function to iterate over an array.
+     * @param {Function} eachFunc The function to iterate over a collection.
+     * @returns {Function} Returns the new each function.
+     */
+    function createForEach(arrayFunc, eachFunc) {
+      return function(collection, iteratee, thisArg) {
+        return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
+          ? arrayFunc(collection, iteratee)
+          : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
+      };
+    }
+
+    /**
+     * Creates a function for `_.forIn` or `_.forInRight`.
+     *
+     * @private
+     * @param {Function} objectFunc The function to iterate over an object.
+     * @returns {Function} Returns the new each function.
+     */
+    function createForIn(objectFunc) {
+      return function(object, iteratee, thisArg) {
+        if (typeof iteratee != 'function' || thisArg !== undefined) {
+          iteratee = bindCallback(iteratee, thisArg, 3);
+        }
+        return objectFunc(object, iteratee, keysIn);
+      };
+    }
+
+    /**
+     * Creates a function for `_.forOwn` or `_.forOwnRight`.
+     *
+     * @private
+     * @param {Function} objectFunc The function to iterate over an object.
+     * @returns {Function} Returns the new each function.
+     */
+    function createForOwn(objectFunc) {
+      return function(object, iteratee, thisArg) {
+        if (typeof iteratee != 'function' || thisArg !== undefined) {
+          iteratee = bindCallback(iteratee, thisArg, 3);
+        }
+        return objectFunc(object, iteratee);
+      };
+    }
+
+    /**
+     * Creates a function for `_.mapKeys` or `_.mapValues`.
+     *
+     * @private
+     * @param {boolean} [isMapKeys] Specify mapping keys instead of values.
+     * @returns {Function} Returns the new map function.
+     */
+    function createObjectMapper(isMapKeys) {
+      return function(object, iteratee, thisArg) {
+        var result = {};
+        iteratee = getCallback(iteratee, thisArg, 3);
+
+        baseForOwn(object, function(value, key, object) {
+          var mapped = iteratee(value, key, object);
+          key = isMapKeys ? mapped : key;
+          value = isMapKeys ? value : mapped;
+          result[key] = value;
+        });
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function for `_.padLeft` or `_.padRight`.
+     *
+     * @private
+     * @param {boolean} [fromRight] Specify padding from the right.
+     * @returns {Function} Returns the new pad function.
+     */
+    function createPadDir(fromRight) {
+      return function(string, length, chars) {
+        string = baseToString(string);
+        return (fromRight ? string : '') + createPadding(string, length, chars) + (fromRight ? '' : string);
+      };
+    }
+
+    /**
+     * Creates a `_.partial` or `_.partialRight` function.
+     *
+     * @private
+     * @param {boolean} flag The partial bit flag.
+     * @returns {Function} Returns the new partial function.
+     */
+    function createPartial(flag) {
+      var partialFunc = restParam(function(func, partials) {
+        var holders = replaceHolders(partials, partialFunc.placeholder);
+        return createWrapper(func, flag, undefined, partials, holders);
+      });
+      return partialFunc;
+    }
+
+    /**
+     * Creates a function for `_.reduce` or `_.reduceRight`.
+     *
+     * @private
+     * @param {Function} arrayFunc The function to iterate over an array.
+     * @param {Function} eachFunc The function to iterate over a collection.
+     * @returns {Function} Returns the new each function.
+     */
+    function createReduce(arrayFunc, eachFunc) {
+      return function(collection, iteratee, accumulator, thisArg) {
+        var initFromArray = arguments.length < 3;
+        return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection))
+          ? arrayFunc(collection, iteratee, accumulator, initFromArray)
+          : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc);
+      };
+    }
+
+    /**
+     * Creates a function that wraps `func` and invokes it with optional `this`
+     * binding of, partial application, and currying.
+     *
+     * @private
+     * @param {Function|string} func The function or method name to reference.
+     * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {Array} [partials] The arguments to prepend to those provided to the new function.
+     * @param {Array} [holders] The `partials` placeholder indexes.
+     * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
+     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
+     * @param {Array} [argPos] The argument positions of the new function.
+     * @param {number} [ary] The arity cap of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
+      var isAry = bitmask & ARY_FLAG,
+          isBind = bitmask & BIND_FLAG,
+          isBindKey = bitmask & BIND_KEY_FLAG,
+          isCurry = bitmask & CURRY_FLAG,
+          isCurryBound = bitmask & CURRY_BOUND_FLAG,
+          isCurryRight = bitmask & CURRY_RIGHT_FLAG,
+          Ctor = isBindKey ? undefined : createCtorWrapper(func);
+
+      function wrapper() {
+        // Avoid `arguments` object use disqualifying optimizations by
+        // converting it to an array before providing it to other functions.
+        var length = arguments.length,
+            index = length,
+            args = Array(length);
+
+        while (index--) {
+          args[index] = arguments[index];
+        }
+        if (partials) {
+          args = composeArgs(args, partials, holders);
+        }
+        if (partialsRight) {
+          args = composeArgsRight(args, partialsRight, holdersRight);
+        }
+        if (isCurry || isCurryRight) {
+          var placeholder = wrapper.placeholder,
+              argsHolders = replaceHolders(args, placeholder);
+
+          length -= argsHolders.length;
+          if (length < arity) {
+            var newArgPos = argPos ? arrayCopy(argPos) : undefined,
+                newArity = nativeMax(arity - length, 0),
+                newsHolders = isCurry ? argsHolders : undefined,
+                newHoldersRight = isCurry ? undefined : argsHolders,
+                newPartials = isCurry ? args : undefined,
+                newPartialsRight = isCurry ? undefined : args;
+
+            bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
+            bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
+
+            if (!isCurryBound) {
+              bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
+            }
+            var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
+                result = createHybridWrapper.apply(undefined, newData);
+
+            if (isLaziable(func)) {
+              setData(result, newData);
+            }
+            result.placeholder = placeholder;
+            return result;
+          }
+        }
+        var thisBinding = isBind ? thisArg : this,
+            fn = isBindKey ? thisBinding[func] : func;
+
+        if (argPos) {
+          args = reorder(args, argPos);
+        }
+        if (isAry && ary < args.length) {
+          args.length = ary;
+        }
+        if (this && this !== root && this instanceof wrapper) {
+          fn = Ctor || createCtorWrapper(func);
+        }
+        return fn.apply(thisBinding, args);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates the padding required for `string` based on the given `length`.
+     * The `chars` string is truncated if the number of characters exceeds `length`.
+     *
+     * @private
+     * @param {string} string The string to create padding for.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the pad for `string`.
+     */
+    function createPadding(string, length, chars) {
+      var strLength = string.length;
+      length = +length;
+
+      if (strLength >= length || !nativeIsFinite(length)) {
+        return '';
+      }
+      var padLength = length - strLength;
+      chars = chars == null ? ' ' : (chars + '');
+      return repeat(chars, nativeCeil(padLength / chars.length)).slice(0, padLength);
+    }
+
+    /**
+     * Creates a function that wraps `func` and invokes it with the optional `this`
+     * binding of `thisArg` and the `partials` prepended to those provided to
+     * the wrapper.
+     *
+     * @private
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
+     * @param {*} thisArg The `this` binding of `func`.
+     * @param {Array} partials The arguments to prepend to those provided to the new function.
+     * @returns {Function} Returns the new bound function.
+     */
+    function createPartialWrapper(func, bitmask, thisArg, partials) {
+      var isBind = bitmask & BIND_FLAG,
+          Ctor = createCtorWrapper(func);
+
+      function wrapper() {
+        // Avoid `arguments` object use disqualifying optimizations by
+        // converting it to an array before providing it `func`.
+        var argsIndex = -1,
+            argsLength = arguments.length,
+            leftIndex = -1,
+            leftLength = partials.length,
+            args = Array(leftLength + argsLength);
+
+        while (++leftIndex < leftLength) {
+          args[leftIndex] = partials[leftIndex];
+        }
+        while (argsLength--) {
+          args[leftIndex++] = arguments[++argsIndex];
+        }
+        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+        return fn.apply(isBind ? thisArg : this, args);
+      }
+      return wrapper;
+    }
+
+    /**
+     * Creates a `_.ceil`, `_.floor`, or `_.round` function.
+     *
+     * @private
+     * @param {string} methodName The name of the `Math` method to use when rounding.
+     * @returns {Function} Returns the new round function.
+     */
+    function createRound(methodName) {
+      var func = Math[methodName];
+      return function(number, precision) {
+        precision = precision === undefined ? 0 : (+precision || 0);
+        if (precision) {
+          precision = pow(10, precision);
+          return func(number * precision) / precision;
+        }
+        return func(number);
+      };
+    }
+
+    /**
+     * Creates a `_.sortedIndex` or `_.sortedLastIndex` function.
+     *
+     * @private
+     * @param {boolean} [retHighest] Specify returning the highest qualified index.
+     * @returns {Function} Returns the new index function.
+     */
+    function createSortedIndex(retHighest) {
+      return function(array, value, iteratee, thisArg) {
+        var callback = getCallback(iteratee);
+        return (iteratee == null && callback === baseCallback)
+          ? binaryIndex(array, value, retHighest)
+          : binaryIndexBy(array, value, callback(iteratee, thisArg, 1), retHighest);
+      };
+    }
+
+    /**
+     * Creates a function that either curries or invokes `func` with optional
+     * `this` binding and partially applied arguments.
+     *
+     * @private
+     * @param {Function|string} func The function or method name to reference.
+     * @param {number} bitmask The bitmask of flags.
+     *  The bitmask may be composed of the following flags:
+     *     1 - `_.bind`
+     *     2 - `_.bindKey`
+     *     4 - `_.curry` or `_.curryRight` of a bound function
+     *     8 - `_.curry`
+     *    16 - `_.curryRight`
+     *    32 - `_.partial`
+     *    64 - `_.partialRight`
+     *   128 - `_.rearg`
+     *   256 - `_.ary`
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {Array} [partials] The arguments to be partially applied.
+     * @param {Array} [holders] The `partials` placeholder indexes.
+     * @param {Array} [argPos] The argument positions of the new function.
+     * @param {number} [ary] The arity cap of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new wrapped function.
+     */
+    function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
+      var isBindKey = bitmask & BIND_KEY_FLAG;
+      if (!isBindKey && typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      var length = partials ? partials.length : 0;
+      if (!length) {
+        bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
+        partials = holders = undefined;
+      }
+      length -= (holders ? holders.length : 0);
+      if (bitmask & PARTIAL_RIGHT_FLAG) {
+        var partialsRight = partials,
+            holdersRight = holders;
+
+        partials = holders = undefined;
+      }
+      var data = isBindKey ? undefined : getData(func),
+          newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
+
+      if (data) {
+        mergeData(newData, data);
+        bitmask = newData[1];
+        arity = newData[9];
+      }
+      newData[9] = arity == null
+        ? (isBindKey ? 0 : func.length)
+        : (nativeMax(arity - length, 0) || 0);
+
+      if (bitmask == BIND_FLAG) {
+        var result = createBindWrapper(newData[0], newData[2]);
+      } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
+        result = createPartialWrapper.apply(undefined, newData);
+      } else {
+        result = createHybridWrapper.apply(undefined, newData);
+      }
+      var setter = data ? baseSetData : setData;
+      return setter(result, newData);
+    }
+
+    /**
+     * A specialized version of `baseIsEqualDeep` for arrays with support for
+     * partial deep comparisons.
+     *
+     * @private
+     * @param {Array} array The array to compare.
+     * @param {Array} other The other array to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} [customizer] The function to customize comparing arrays.
+     * @param {boolean} [isLoose] Specify performing partial comparisons.
+     * @param {Array} [stackA] Tracks traversed `value` objects.
+     * @param {Array} [stackB] Tracks traversed `other` objects.
+     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+     */
+    function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
+      var index = -1,
+          arrLength = array.length,
+          othLength = other.length;
+
+      if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
+        return false;
+      }
+      // Ignore non-index properties.
+      while (++index < arrLength) {
+        var arrValue = array[index],
+            othValue = other[index],
+            result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
+
+        if (result !== undefined) {
+          if (result) {
+            continue;
+          }
+          return false;
+        }
+        // Recursively compare arrays (susceptible to call stack limits).
+        if (isLoose) {
+          if (!arraySome(other, function(othValue) {
+                return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
+              })) {
+            return false;
+          }
+        } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * A specialized version of `baseIsEqualDeep` for comparing objects of
+     * the same `toStringTag`.
+     *
+     * **Note:** This function only supports comparing values with tags of
+     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {string} tag The `toStringTag` of the objects to compare.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function equalByTag(object, other, tag) {
+      switch (tag) {
+        case boolTag:
+        case dateTag:
+          // Coerce dates and booleans to numbers, dates to milliseconds and booleans
+          // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
+          return +object == +other;
+
+        case errorTag:
+          return object.name == other.name && object.message == other.message;
+
+        case numberTag:
+          // Treat `NaN` vs. `NaN` as equal.
+          return (object != +object)
+            ? other != +other
+            : object == +other;
+
+        case regexpTag:
+        case stringTag:
+          // Coerce regexes to strings and treat strings primitives and string
+          // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
+          return object == (other + '');
+      }
+      return false;
+    }
+
+    /**
+     * A specialized version of `baseIsEqualDeep` for objects with support for
+     * partial deep comparisons.
+     *
+     * @private
+     * @param {Object} object The object to compare.
+     * @param {Object} other The other object to compare.
+     * @param {Function} equalFunc The function to determine equivalents of values.
+     * @param {Function} [customizer] The function to customize comparing values.
+     * @param {boolean} [isLoose] Specify performing partial comparisons.
+     * @param {Array} [stackA] Tracks traversed `value` objects.
+     * @param {Array} [stackB] Tracks traversed `other` objects.
+     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+     */
+    function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
+      var objProps = keys(object),
+          objLength = objProps.length,
+          othProps = keys(other),
+          othLength = othProps.length;
+
+      if (objLength != othLength && !isLoose) {
+        return false;
+      }
+      var index = objLength;
+      while (index--) {
+        var key = objProps[index];
+        if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
+          return false;
+        }
+      }
+      var skipCtor = isLoose;
+      while (++index < objLength) {
+        key = objProps[index];
+        var objValue = object[key],
+            othValue = other[key],
+            result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
+
+        // Recursively compare objects (susceptible to call stack limits).
+        if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
+          return false;
+        }
+        skipCtor || (skipCtor = key == 'constructor');
+      }
+      if (!skipCtor) {
+        var objCtor = object.constructor,
+            othCtor = other.constructor;
+
+        // Non `Object` object instances with different constructors are not equal.
+        if (objCtor != othCtor &&
+            ('constructor' in object && 'constructor' in other) &&
+            !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+              typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * Gets the appropriate "callback" function. If the `_.callback` method is
+     * customized this function returns the custom method, otherwise it returns
+     * the `baseCallback` function. If arguments are provided the chosen function
+     * is invoked with them and its result is returned.
+     *
+     * @private
+     * @returns {Function} Returns the chosen function or its result.
+     */
+    function getCallback(func, thisArg, argCount) {
+      var result = lodash.callback || callback;
+      result = result === callback ? baseCallback : result;
+      return argCount ? result(func, thisArg, argCount) : result;
+    }
+
+    /**
+     * Gets metadata for `func`.
+     *
+     * @private
+     * @param {Function} func The function to query.
+     * @returns {*} Returns the metadata for `func`.
+     */
+    var getData = !metaMap ? noop : function(func) {
+      return metaMap.get(func);
+    };
+
+    /**
+     * Gets the name of `func`.
+     *
+     * @private
+     * @param {Function} func The function to query.
+     * @returns {string} Returns the function name.
+     */
+    function getFuncName(func) {
+      var result = (func.name + ''),
+          array = realNames[result],
+          length = array ? array.length : 0;
+
+      while (length--) {
+        var data = array[length],
+            otherFunc = data.func;
+        if (otherFunc == null || otherFunc == func) {
+          return data.name;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+     * customized this function returns the custom method, otherwise it returns
+     * the `baseIndexOf` function. If arguments are provided the chosen function
+     * is invoked with them and its result is returned.
+     *
+     * @private
+     * @returns {Function|number} Returns the chosen function or its result.
+     */
+    function getIndexOf(collection, target, fromIndex) {
+      var result = lodash.indexOf || indexOf;
+      result = result === indexOf ? baseIndexOf : result;
+      return collection ? result(collection, target, fromIndex) : result;
+    }
+
+    /**
+     * Gets the "length" property value of `object`.
+     *
+     * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
+     * that affects Safari on at least iOS 8.1-8.3 ARM64.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {*} Returns the "length" value.
+     */
+    var getLength = baseProperty('length');
+
+    /**
+     * Gets the propery names, values, and compare flags of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the match data of `object`.
+     */
+    function getMatchData(object) {
+      var result = pairs(object),
+          length = result.length;
+
+      while (length--) {
+        result[length][2] = isStrictComparable(result[length][1]);
+      }
+      return result;
+    }
+
+    /**
+     * Gets the native function at `key` of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {string} key The key of the method to get.
+     * @returns {*} Returns the function if it's native, else `undefined`.
+     */
+    function getNative(object, key) {
+      var value = object == null ? undefined : object[key];
+      return isNative(value) ? value : undefined;
+    }
+
+    /**
+     * Gets the view, applying any `transforms` to the `start` and `end` positions.
+     *
+     * @private
+     * @param {number} start The start of the view.
+     * @param {number} end The end of the view.
+     * @param {Array} transforms The transformations to apply to the view.
+     * @returns {Object} Returns an object containing the `start` and `end`
+     *  positions of the view.
+     */
+    function getView(start, end, transforms) {
+      var index = -1,
+          length = transforms.length;
+
+      while (++index < length) {
+        var data = transforms[index],
+            size = data.size;
+
+        switch (data.type) {
+          case 'drop':      start += size; break;
+          case 'dropRight': end -= size; break;
+          case 'take':      end = nativeMin(end, start + size); break;
+          case 'takeRight': start = nativeMax(start, end - size); break;
+        }
+      }
+      return { 'start': start, 'end': end };
+    }
+
+    /**
+     * Initializes an array clone.
+     *
+     * @private
+     * @param {Array} array The array to clone.
+     * @returns {Array} Returns the initialized clone.
+     */
+    function initCloneArray(array) {
+      var length = array.length,
+          result = new array.constructor(length);
+
+      // Add array properties assigned by `RegExp#exec`.
+      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
+        result.index = array.index;
+        result.input = array.input;
+      }
+      return result;
+    }
+
+    /**
+     * Initializes an object clone.
+     *
+     * @private
+     * @param {Object} object The object to clone.
+     * @returns {Object} Returns the initialized clone.
+     */
+    function initCloneObject(object) {
+      var Ctor = object.constructor;
+      if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
+        Ctor = Object;
+      }
+      return new Ctor;
+    }
+
+    /**
+     * Initializes an object clone based on its `toStringTag`.
+     *
+     * **Note:** This function only supports cloning values with tags of
+     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+     *
+     * @private
+     * @param {Object} object The object to clone.
+     * @param {string} tag The `toStringTag` of the object to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @returns {Object} Returns the initialized clone.
+     */
+    function initCloneByTag(object, tag, isDeep) {
+      var Ctor = object.constructor;
+      switch (tag) {
+        case arrayBufferTag:
+          return bufferClone(object);
+
+        case boolTag:
+        case dateTag:
+          return new Ctor(+object);
+
+        case float32Tag: case float64Tag:
+        case int8Tag: case int16Tag: case int32Tag:
+        case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
+          var buffer = object.buffer;
+          return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
+
+        case numberTag:
+        case stringTag:
+          return new Ctor(object);
+
+        case regexpTag:
+          var result = new Ctor(object.source, reFlags.exec(object));
+          result.lastIndex = object.lastIndex;
+      }
+      return result;
+    }
+
+    /**
+     * Invokes the method at `path` on `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the method to invoke.
+     * @param {Array} args The arguments to invoke the method with.
+     * @returns {*} Returns the result of the invoked method.
+     */
+    function invokePath(object, path, args) {
+      if (object != null && !isKey(path, object)) {
+        path = toPath(path);
+        object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
+        path = last(path);
+      }
+      var func = object == null ? object : object[path];
+      return func == null ? undefined : func.apply(object, args);
+    }
+
+    /**
+     * Checks if `value` is array-like.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+     */
+    function isArrayLike(value) {
+      return value != null && isLength(getLength(value));
+    }
+
+    /**
+     * Checks if `value` is a valid array-like index.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+     */
+    function isIndex(value, length) {
+      value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
+      length = length == null ? MAX_SAFE_INTEGER : length;
+      return value > -1 && value % 1 == 0 && value < length;
+    }
+
+    /**
+     * Checks if the provided arguments are from an iteratee call.
+     *
+     * @private
+     * @param {*} value The potential iteratee value argument.
+     * @param {*} index The potential iteratee index or key argument.
+     * @param {*} object The potential iteratee object argument.
+     * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
+     */
+    function isIterateeCall(value, index, object) {
+      if (!isObject(object)) {
+        return false;
+      }
+      var type = typeof index;
+      if (type == 'number'
+          ? (isArrayLike(object) && isIndex(index, object.length))
+          : (type == 'string' && index in object)) {
+        var other = object[index];
+        return value === value ? (value === other) : (other !== other);
+      }
+      return false;
+    }
+
+    /**
+     * Checks if `value` is a property name and not a property path.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @param {Object} [object] The object to query keys on.
+     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
+     */
+    function isKey(value, object) {
+      var type = typeof value;
+      if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') {
+        return true;
+      }
+      if (isArray(value)) {
+        return false;
+      }
+      var result = !reIsDeepProp.test(value);
+      return result || (object != null && value in toObject(object));
+    }
+
+    /**
+     * Checks if `func` has a lazy counterpart.
+     *
+     * @private
+     * @param {Function} func The function to check.
+     * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
+     */
+    function isLaziable(func) {
+      var funcName = getFuncName(func),
+          other = lodash[funcName];
+
+      if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
+        return false;
+      }
+      if (func === other) {
+        return true;
+      }
+      var data = getData(other);
+      return !!data && func === data[0];
+    }
+
+    /**
+     * Checks if `value` is a valid array-like length.
+     *
+     * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+     */
+    function isLength(value) {
+      return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+    }
+
+    /**
+     * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` if suitable for strict
+     *  equality comparisons, else `false`.
+     */
+    function isStrictComparable(value) {
+      return value === value && !isObject(value);
+    }
+
+    /**
+     * Merges the function metadata of `source` into `data`.
+     *
+     * Merging metadata reduces the number of wrappers required to invoke a function.
+     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
+     * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
+     * augment function arguments, making the order in which they are executed important,
+     * preventing the merging of metadata. However, we make an exception for a safe
+     * common case where curried functions have `_.ary` and or `_.rearg` applied.
+     *
+     * @private
+     * @param {Array} data The destination metadata.
+     * @param {Array} source The source metadata.
+     * @returns {Array} Returns `data`.
+     */
+    function mergeData(data, source) {
+      var bitmask = data[1],
+          srcBitmask = source[1],
+          newBitmask = bitmask | srcBitmask,
+          isCommon = newBitmask < ARY_FLAG;
+
+      var isCombo =
+        (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
+        (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
+        (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
+
+      // Exit early if metadata can't be merged.
+      if (!(isCommon || isCombo)) {
+        return data;
+      }
+      // Use source `thisArg` if available.
+      if (srcBitmask & BIND_FLAG) {
+        data[2] = source[2];
+        // Set when currying a bound function.
+        newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
+      }
+      // Compose partial arguments.
+      var value = source[3];
+      if (value) {
+        var partials = data[3];
+        data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
+        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
+      }
+      // Compose partial right arguments.
+      value = source[5];
+      if (value) {
+        partials = data[5];
+        data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
+        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
+      }
+      // Use source `argPos` if available.
+      value = source[7];
+      if (value) {
+        data[7] = arrayCopy(value);
+      }
+      // Use source `ary` if it's smaller.
+      if (srcBitmask & ARY_FLAG) {
+        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
+      }
+      // Use source `arity` if one is not provided.
+      if (data[9] == null) {
+        data[9] = source[9];
+      }
+      // Use source `func` and merge bitmasks.
+      data[0] = source[0];
+      data[1] = newBitmask;
+
+      return data;
+    }
+
+    /**
+     * Used by `_.defaultsDeep` to customize its `_.merge` use.
+     *
+     * @private
+     * @param {*} objectValue The destination object property value.
+     * @param {*} sourceValue The source object property value.
+     * @returns {*} Returns the value to assign to the destination object.
+     */
+    function mergeDefaults(objectValue, sourceValue) {
+      return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults);
+    }
+
+    /**
+     * A specialized version of `_.pick` which picks `object` properties specified
+     * by `props`.
+     *
+     * @private
+     * @param {Object} object The source object.
+     * @param {string[]} props The property names to pick.
+     * @returns {Object} Returns the new object.
+     */
+    function pickByArray(object, props) {
+      object = toObject(object);
+
+      var index = -1,
+          length = props.length,
+          result = {};
+
+      while (++index < length) {
+        var key = props[index];
+        if (key in object) {
+          result[key] = object[key];
+        }
+      }
+      return result;
+    }
+
+    /**
+     * A specialized version of `_.pick` which picks `object` properties `predicate`
+     * returns truthy for.
+     *
+     * @private
+     * @param {Object} object The source object.
+     * @param {Function} predicate The function invoked per iteration.
+     * @returns {Object} Returns the new object.
+     */
+    function pickByCallback(object, predicate) {
+      var result = {};
+      baseForIn(object, function(value, key, object) {
+        if (predicate(value, key, object)) {
+          result[key] = value;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * Reorder `array` according to the specified indexes where the element at
+     * the first index is assigned as the first element, the element at
+     * the second index is assigned as the second element, and so on.
+     *
+     * @private
+     * @param {Array} array The array to reorder.
+     * @param {Array} indexes The arranged array indexes.
+     * @returns {Array} Returns `array`.
+     */
+    function reorder(array, indexes) {
+      var arrLength = array.length,
+          length = nativeMin(indexes.length, arrLength),
+          oldArray = arrayCopy(array);
+
+      while (length--) {
+        var index = indexes[length];
+        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
+      }
+      return array;
+    }
+
+    /**
+     * Sets metadata for `func`.
+     *
+     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
+     * period of time, it will trip its breaker and transition to an identity function
+     * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
+     * for more details.
+     *
+     * @private
+     * @param {Function} func The function to associate metadata with.
+     * @param {*} data The metadata.
+     * @returns {Function} Returns `func`.
+     */
+    var setData = (function() {
+      var count = 0,
+          lastCalled = 0;
+
+      return function(key, value) {
+        var stamp = now(),
+            remaining = HOT_SPAN - (stamp - lastCalled);
+
+        lastCalled = stamp;
+        if (remaining > 0) {
+          if (++count >= HOT_COUNT) {
+            return key;
+          }
+        } else {
+          count = 0;
+        }
+        return baseSetData(key, value);
+      };
+    }());
+
+    /**
+     * A fallback implementation of `Object.keys` which creates an array of the
+     * own enumerable property names of `object`.
+     *
+     * @private
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     */
+    function shimKeys(object) {
+      var props = keysIn(object),
+          propsLength = props.length,
+          length = propsLength && object.length;
+
+      var allowIndexes = !!length && isLength(length) &&
+        (isArray(object) || isArguments(object));
+
+      var index = -1,
+          result = [];
+
+      while (++index < propsLength) {
+        var key = props[index];
+        if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
+          result.push(key);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Converts `value` to an array-like object if it's not one.
+     *
+     * @private
+     * @param {*} value The value to process.
+     * @returns {Array|Object} Returns the array-like object.
+     */
+    function toIterable(value) {
+      if (value == null) {
+        return [];
+      }
+      if (!isArrayLike(value)) {
+        return values(value);
+      }
+      return isObject(value) ? value : Object(value);
+    }
+
+    /**
+     * Converts `value` to an object if it's not one.
+     *
+     * @private
+     * @param {*} value The value to process.
+     * @returns {Object} Returns the object.
+     */
+    function toObject(value) {
+      return isObject(value) ? value : Object(value);
+    }
+
+    /**
+     * Converts `value` to property path array if it's not one.
+     *
+     * @private
+     * @param {*} value The value to process.
+     * @returns {Array} Returns the property path array.
+     */
+    function toPath(value) {
+      if (isArray(value)) {
+        return value;
+      }
+      var result = [];
+      baseToString(value).replace(rePropName, function(match, number, quote, string) {
+        result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
+      });
+      return result;
+    }
+
+    /**
+     * Creates a clone of `wrapper`.
+     *
+     * @private
+     * @param {Object} wrapper The wrapper to clone.
+     * @returns {Object} Returns the cloned wrapper.
+     */
+    function wrapperClone(wrapper) {
+      return wrapper instanceof LazyWrapper
+        ? wrapper.clone()
+        : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array of elements split into groups the length of `size`.
+     * If `collection` can't be split evenly, the final chunk will be the remaining
+     * elements.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to process.
+     * @param {number} [size=1] The length of each chunk.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the new array containing chunks.
+     * @example
+     *
+     * _.chunk(['a', 'b', 'c', 'd'], 2);
+     * // => [['a', 'b'], ['c', 'd']]
+     *
+     * _.chunk(['a', 'b', 'c', 'd'], 3);
+     * // => [['a', 'b', 'c'], ['d']]
+     */
+    function chunk(array, size, guard) {
+      if (guard ? isIterateeCall(array, size, guard) : size == null) {
+        size = 1;
+      } else {
+        size = nativeMax(nativeFloor(size) || 1, 1);
+      }
+      var index = 0,
+          length = array ? array.length : 0,
+          resIndex = -1,
+          result = Array(nativeCeil(length / size));
+
+      while (index < length) {
+        result[++resIndex] = baseSlice(array, index, (index += size));
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array with all falsey values removed. The values `false`, `null`,
+     * `0`, `""`, `undefined`, and `NaN` are falsey.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to compact.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * _.compact([0, 1, false, 2, '', 3]);
+     * // => [1, 2, 3]
+     */
+    function compact(array) {
+      var index = -1,
+          length = array ? array.length : 0,
+          resIndex = -1,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+        if (value) {
+          result[++resIndex] = value;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array of unique `array` values not included in the other
+     * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {...Array} [values] The arrays of values to exclude.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * _.difference([1, 2, 3], [4, 2]);
+     * // => [1, 3]
+     */
+    var difference = restParam(function(array, values) {
+      return (isObjectLike(array) && isArrayLike(array))
+        ? baseDifference(array, baseFlatten(values, false, true))
+        : [];
+    });
+
+    /**
+     * Creates a slice of `array` with `n` elements dropped from the beginning.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to drop.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.drop([1, 2, 3]);
+     * // => [2, 3]
+     *
+     * _.drop([1, 2, 3], 2);
+     * // => [3]
+     *
+     * _.drop([1, 2, 3], 5);
+     * // => []
+     *
+     * _.drop([1, 2, 3], 0);
+     * // => [1, 2, 3]
+     */
+    function drop(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (guard ? isIterateeCall(array, n, guard) : n == null) {
+        n = 1;
+      }
+      return baseSlice(array, n < 0 ? 0 : n);
+    }
+
+    /**
+     * Creates a slice of `array` with `n` elements dropped from the end.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to drop.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.dropRight([1, 2, 3]);
+     * // => [1, 2]
+     *
+     * _.dropRight([1, 2, 3], 2);
+     * // => [1]
+     *
+     * _.dropRight([1, 2, 3], 5);
+     * // => []
+     *
+     * _.dropRight([1, 2, 3], 0);
+     * // => [1, 2, 3]
+     */
+    function dropRight(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (guard ? isIterateeCall(array, n, guard) : n == null) {
+        n = 1;
+      }
+      n = length - (+n || 0);
+      return baseSlice(array, 0, n < 0 ? 0 : n);
+    }
+
+    /**
+     * Creates a slice of `array` excluding elements dropped from the end.
+     * Elements are dropped until `predicate` returns falsey. The predicate is
+     * bound to `thisArg` and invoked with three arguments: (value, index, array).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that match the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.dropRightWhile([1, 2, 3], function(n) {
+     *   return n > 1;
+     * });
+     * // => [1]
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': true },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': false }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user');
+     * // => ['barney', 'fred']
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.pluck(_.dropRightWhile(users, 'active', false), 'user');
+     * // => ['barney']
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.dropRightWhile(users, 'active'), 'user');
+     * // => ['barney', 'fred', 'pebbles']
+     */
+    function dropRightWhile(array, predicate, thisArg) {
+      return (array && array.length)
+        ? baseWhile(array, getCallback(predicate, thisArg, 3), true, true)
+        : [];
+    }
+
+    /**
+     * Creates a slice of `array` excluding elements dropped from the beginning.
+     * Elements are dropped until `predicate` returns falsey. The predicate is
+     * bound to `thisArg` and invoked with three arguments: (value, index, array).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.dropWhile([1, 2, 3], function(n) {
+     *   return n < 3;
+     * });
+     * // => [3]
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': false },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': true }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user');
+     * // => ['fred', 'pebbles']
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.pluck(_.dropWhile(users, 'active', false), 'user');
+     * // => ['pebbles']
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.dropWhile(users, 'active'), 'user');
+     * // => ['barney', 'fred', 'pebbles']
+     */
+    function dropWhile(array, predicate, thisArg) {
+      return (array && array.length)
+        ? baseWhile(array, getCallback(predicate, thisArg, 3), true)
+        : [];
+    }
+
+    /**
+     * Fills elements of `array` with `value` from `start` up to, but not
+     * including, `end`.
+     *
+     * **Note:** This method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to fill.
+     * @param {*} value The value to fill `array` with.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [1, 2, 3];
+     *
+     * _.fill(array, 'a');
+     * console.log(array);
+     * // => ['a', 'a', 'a']
+     *
+     * _.fill(Array(3), 2);
+     * // => [2, 2, 2]
+     *
+     * _.fill([4, 6, 8], '*', 1, 2);
+     * // => [4, '*', 8]
+     */
+    function fill(array, value, start, end) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
+        start = 0;
+        end = length;
+      }
+      return baseFill(array, value, start, end);
+    }
+
+    /**
+     * This method is like `_.find` except that it returns the index of the first
+     * element `predicate` returns truthy for instead of the element itself.
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': false },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': true }
+     * ];
+     *
+     * _.findIndex(users, function(chr) {
+     *   return chr.user == 'barney';
+     * });
+     * // => 0
+     *
+     * // using the `_.matches` callback shorthand
+     * _.findIndex(users, { 'user': 'fred', 'active': false });
+     * // => 1
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.findIndex(users, 'active', false);
+     * // => 0
+     *
+     * // using the `_.property` callback shorthand
+     * _.findIndex(users, 'active');
+     * // => 2
+     */
+    var findIndex = createFindIndex();
+
+    /**
+     * This method is like `_.findIndex` except that it iterates over elements
+     * of `collection` from right to left.
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': true },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': false }
+     * ];
+     *
+     * _.findLastIndex(users, function(chr) {
+     *   return chr.user == 'pebbles';
+     * });
+     * // => 2
+     *
+     * // using the `_.matches` callback shorthand
+     * _.findLastIndex(users, { 'user': 'barney', 'active': true });
+     * // => 0
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.findLastIndex(users, 'active', false);
+     * // => 2
+     *
+     * // using the `_.property` callback shorthand
+     * _.findLastIndex(users, 'active');
+     * // => 0
+     */
+    var findLastIndex = createFindIndex(true);
+
+    /**
+     * Gets the first element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @alias head
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {*} Returns the first element of `array`.
+     * @example
+     *
+     * _.first([1, 2, 3]);
+     * // => 1
+     *
+     * _.first([]);
+     * // => undefined
+     */
+    function first(array) {
+      return array ? array[0] : undefined;
+    }
+
+    /**
+     * Flattens a nested array. If `isDeep` is `true` the array is recursively
+     * flattened, otherwise it's only flattened a single level.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to flatten.
+     * @param {boolean} [isDeep] Specify a deep flatten.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * _.flatten([1, [2, 3, [4]]]);
+     * // => [1, 2, 3, [4]]
+     *
+     * // using `isDeep`
+     * _.flatten([1, [2, 3, [4]]], true);
+     * // => [1, 2, 3, 4]
+     */
+    function flatten(array, isDeep, guard) {
+      var length = array ? array.length : 0;
+      if (guard && isIterateeCall(array, isDeep, guard)) {
+        isDeep = false;
+      }
+      return length ? baseFlatten(array, isDeep) : [];
+    }
+
+    /**
+     * Recursively flattens a nested array.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to recursively flatten.
+     * @returns {Array} Returns the new flattened array.
+     * @example
+     *
+     * _.flattenDeep([1, [2, 3, [4]]]);
+     * // => [1, 2, 3, 4]
+     */
+    function flattenDeep(array) {
+      var length = array ? array.length : 0;
+      return length ? baseFlatten(array, true) : [];
+    }
+
+    /**
+     * Gets the index at which the first occurrence of `value` is found in `array`
+     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons. If `fromIndex` is negative, it's used as the offset
+     * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
+     * performs a faster binary search.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+     *  to perform a binary search on a sorted array.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     * @example
+     *
+     * _.indexOf([1, 2, 1, 2], 2);
+     * // => 1
+     *
+     * // using `fromIndex`
+     * _.indexOf([1, 2, 1, 2], 2, 2);
+     * // => 3
+     *
+     * // performing a binary search
+     * _.indexOf([1, 1, 2, 2], 2, true);
+     * // => 2
+     */
+    function indexOf(array, value, fromIndex) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return -1;
+      }
+      if (typeof fromIndex == 'number') {
+        fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
+      } else if (fromIndex) {
+        var index = binaryIndex(array, value);
+        if (index < length &&
+            (value === value ? (value === array[index]) : (array[index] !== array[index]))) {
+          return index;
+        }
+        return -1;
+      }
+      return baseIndexOf(array, value, fromIndex || 0);
+    }
+
+    /**
+     * Gets all but the last element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.initial([1, 2, 3]);
+     * // => [1, 2]
+     */
+    function initial(array) {
+      return dropRight(array, 1);
+    }
+
+    /**
+     * Creates an array of unique values that are included in all of the provided
+     * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @returns {Array} Returns the new array of shared values.
+     * @example
+     * _.intersection([1, 2], [4, 2], [2, 1]);
+     * // => [2]
+     */
+    var intersection = restParam(function(arrays) {
+      var othLength = arrays.length,
+          othIndex = othLength,
+          caches = Array(length),
+          indexOf = getIndexOf(),
+          isCommon = indexOf === baseIndexOf,
+          result = [];
+
+      while (othIndex--) {
+        var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : [];
+        caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null;
+      }
+      var array = arrays[0],
+          index = -1,
+          length = array ? array.length : 0,
+          seen = caches[0];
+
+      outer:
+      while (++index < length) {
+        value = array[index];
+        if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) {
+          var othIndex = othLength;
+          while (--othIndex) {
+            var cache = caches[othIndex];
+            if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) {
+              continue outer;
+            }
+          }
+          if (seen) {
+            seen.push(value);
+          }
+          result.push(value);
+        }
+      }
+      return result;
+    });
+
+    /**
+     * Gets the last element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {*} Returns the last element of `array`.
+     * @example
+     *
+     * _.last([1, 2, 3]);
+     * // => 3
+     */
+    function last(array) {
+      var length = array ? array.length : 0;
+      return length ? array[length - 1] : undefined;
+    }
+
+    /**
+     * This method is like `_.indexOf` except that it iterates over elements of
+     * `array` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {boolean|number} [fromIndex=array.length-1] The index to search from
+     *  or `true` to perform a binary search on a sorted array.
+     * @returns {number} Returns the index of the matched value, else `-1`.
+     * @example
+     *
+     * _.lastIndexOf([1, 2, 1, 2], 2);
+     * // => 3
+     *
+     * // using `fromIndex`
+     * _.lastIndexOf([1, 2, 1, 2], 2, 2);
+     * // => 1
+     *
+     * // performing a binary search
+     * _.lastIndexOf([1, 1, 2, 2], 2, true);
+     * // => 3
+     */
+    function lastIndexOf(array, value, fromIndex) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return -1;
+      }
+      var index = length;
+      if (typeof fromIndex == 'number') {
+        index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1;
+      } else if (fromIndex) {
+        index = binaryIndex(array, value, true) - 1;
+        var other = array[index];
+        if (value === value ? (value === other) : (other !== other)) {
+          return index;
+        }
+        return -1;
+      }
+      if (value !== value) {
+        return indexOfNaN(array, index, true);
+      }
+      while (index--) {
+        if (array[index] === value) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Removes all provided values from `array` using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * **Note:** Unlike `_.without`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {...*} [values] The values to remove.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [1, 2, 3, 1, 2, 3];
+     *
+     * _.pull(array, 2, 3);
+     * console.log(array);
+     * // => [1, 1]
+     */
+    function pull() {
+      var args = arguments,
+          array = args[0];
+
+      if (!(array && array.length)) {
+        return array;
+      }
+      var index = 0,
+          indexOf = getIndexOf(),
+          length = args.length;
+
+      while (++index < length) {
+        var fromIndex = 0,
+            value = args[index];
+
+        while ((fromIndex = indexOf(array, value, fromIndex)) > -1) {
+          splice.call(array, fromIndex, 1);
+        }
+      }
+      return array;
+    }
+
+    /**
+     * Removes elements from `array` corresponding to the given indexes and returns
+     * an array of the removed elements. Indexes may be specified as an array of
+     * indexes or as individual arguments.
+     *
+     * **Note:** Unlike `_.at`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {...(number|number[])} [indexes] The indexes of elements to remove,
+     *  specified as individual indexes or arrays of indexes.
+     * @returns {Array} Returns the new array of removed elements.
+     * @example
+     *
+     * var array = [5, 10, 15, 20];
+     * var evens = _.pullAt(array, 1, 3);
+     *
+     * console.log(array);
+     * // => [5, 15]
+     *
+     * console.log(evens);
+     * // => [10, 20]
+     */
+    var pullAt = restParam(function(array, indexes) {
+      indexes = baseFlatten(indexes);
+
+      var result = baseAt(array, indexes);
+      basePullAt(array, indexes.sort(baseCompareAscending));
+      return result;
+    });
+
+    /**
+     * Removes all elements from `array` that `predicate` returns truthy for
+     * and returns an array of the removed elements. The predicate is bound to
+     * `thisArg` and invoked with three arguments: (value, index, array).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * **Note:** Unlike `_.filter`, this method mutates `array`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to modify.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the new array of removed elements.
+     * @example
+     *
+     * var array = [1, 2, 3, 4];
+     * var evens = _.remove(array, function(n) {
+     *   return n % 2 == 0;
+     * });
+     *
+     * console.log(array);
+     * // => [1, 3]
+     *
+     * console.log(evens);
+     * // => [2, 4]
+     */
+    function remove(array, predicate, thisArg) {
+      var result = [];
+      if (!(array && array.length)) {
+        return result;
+      }
+      var index = -1,
+          indexes = [],
+          length = array.length;
+
+      predicate = getCallback(predicate, thisArg, 3);
+      while (++index < length) {
+        var value = array[index];
+        if (predicate(value, index, array)) {
+          result.push(value);
+          indexes.push(index);
+        }
+      }
+      basePullAt(array, indexes);
+      return result;
+    }
+
+    /**
+     * Gets all but the first element of `array`.
+     *
+     * @static
+     * @memberOf _
+     * @alias tail
+     * @category Array
+     * @param {Array} array The array to query.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.rest([1, 2, 3]);
+     * // => [2, 3]
+     */
+    function rest(array) {
+      return drop(array, 1);
+    }
+
+    /**
+     * Creates a slice of `array` from `start` up to, but not including, `end`.
+     *
+     * **Note:** This method is used instead of `Array#slice` to support node
+     * lists in IE < 9 and to ensure dense arrays are returned.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to slice.
+     * @param {number} [start=0] The start position.
+     * @param {number} [end=array.length] The end position.
+     * @returns {Array} Returns the slice of `array`.
+     */
+    function slice(array, start, end) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
+        start = 0;
+        end = length;
+      }
+      return baseSlice(array, start, end);
+    }
+
+    /**
+     * Uses a binary search to determine the lowest index at which `value` should
+     * be inserted into `array` in order to maintain its sort order. If an iteratee
+     * function is provided it's invoked for `value` and each element of `array`
+     * to compute their sort ranking. The iteratee is bound to `thisArg` and
+     * invoked with one argument; (value).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * _.sortedIndex([30, 50], 40);
+     * // => 1
+     *
+     * _.sortedIndex([4, 4, 5, 5], 5);
+     * // => 2
+     *
+     * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } };
+     *
+     * // using an iteratee function
+     * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) {
+     *   return this.data[word];
+     * }, dict);
+     * // => 1
+     *
+     * // using the `_.property` callback shorthand
+     * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+     * // => 1
+     */
+    var sortedIndex = createSortedIndex();
+
+    /**
+     * This method is like `_.sortedIndex` except that it returns the highest
+     * index at which `value` should be inserted into `array` in order to
+     * maintain its sort order.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The sorted array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * _.sortedLastIndex([4, 4, 5, 5], 5);
+     * // => 4
+     */
+    var sortedLastIndex = createSortedIndex(true);
+
+    /**
+     * Creates a slice of `array` with `n` elements taken from the beginning.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to take.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.take([1, 2, 3]);
+     * // => [1]
+     *
+     * _.take([1, 2, 3], 2);
+     * // => [1, 2]
+     *
+     * _.take([1, 2, 3], 5);
+     * // => [1, 2, 3]
+     *
+     * _.take([1, 2, 3], 0);
+     * // => []
+     */
+    function take(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (guard ? isIterateeCall(array, n, guard) : n == null) {
+        n = 1;
+      }
+      return baseSlice(array, 0, n < 0 ? 0 : n);
+    }
+
+    /**
+     * Creates a slice of `array` with `n` elements taken from the end.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {number} [n=1] The number of elements to take.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.takeRight([1, 2, 3]);
+     * // => [3]
+     *
+     * _.takeRight([1, 2, 3], 2);
+     * // => [2, 3]
+     *
+     * _.takeRight([1, 2, 3], 5);
+     * // => [1, 2, 3]
+     *
+     * _.takeRight([1, 2, 3], 0);
+     * // => []
+     */
+    function takeRight(array, n, guard) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (guard ? isIterateeCall(array, n, guard) : n == null) {
+        n = 1;
+      }
+      n = length - (+n || 0);
+      return baseSlice(array, n < 0 ? 0 : n);
+    }
+
+    /**
+     * Creates a slice of `array` with elements taken from the end. Elements are
+     * taken until `predicate` returns falsey. The predicate is bound to `thisArg`
+     * and invoked with three arguments: (value, index, array).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.takeRightWhile([1, 2, 3], function(n) {
+     *   return n > 1;
+     * });
+     * // => [2, 3]
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': true },
+     *   { 'user': 'fred',    'active': false },
+     *   { 'user': 'pebbles', 'active': false }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user');
+     * // => ['pebbles']
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.pluck(_.takeRightWhile(users, 'active', false), 'user');
+     * // => ['fred', 'pebbles']
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.takeRightWhile(users, 'active'), 'user');
+     * // => []
+     */
+    function takeRightWhile(array, predicate, thisArg) {
+      return (array && array.length)
+        ? baseWhile(array, getCallback(predicate, thisArg, 3), false, true)
+        : [];
+    }
+
+    /**
+     * Creates a slice of `array` with elements taken from the beginning. Elements
+     * are taken until `predicate` returns falsey. The predicate is bound to
+     * `thisArg` and invoked with three arguments: (value, index, array).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to query.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the slice of `array`.
+     * @example
+     *
+     * _.takeWhile([1, 2, 3], function(n) {
+     *   return n < 3;
+     * });
+     * // => [1, 2]
+     *
+     * var users = [
+     *   { 'user': 'barney',  'active': false },
+     *   { 'user': 'fred',    'active': false},
+     *   { 'user': 'pebbles', 'active': true }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user');
+     * // => ['barney']
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.pluck(_.takeWhile(users, 'active', false), 'user');
+     * // => ['barney', 'fred']
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.takeWhile(users, 'active'), 'user');
+     * // => []
+     */
+    function takeWhile(array, predicate, thisArg) {
+      return (array && array.length)
+        ? baseWhile(array, getCallback(predicate, thisArg, 3))
+        : [];
+    }
+
+    /**
+     * Creates an array of unique values, in order, from all of the provided arrays
+     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @returns {Array} Returns the new array of combined values.
+     * @example
+     *
+     * _.union([1, 2], [4, 2], [2, 1]);
+     * // => [1, 2, 4]
+     */
+    var union = restParam(function(arrays) {
+      return baseUniq(baseFlatten(arrays, false, true));
+    });
+
+    /**
+     * Creates a duplicate-free version of an array, using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons, in which only the first occurence of each element
+     * is kept. Providing `true` for `isSorted` performs a faster search algorithm
+     * for sorted arrays. If an iteratee function is provided it's invoked for
+     * each element in the array to generate the criterion by which uniqueness
+     * is computed. The `iteratee` is bound to `thisArg` and invoked with three
+     * arguments: (value, index, array).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias unique
+     * @category Array
+     * @param {Array} array The array to inspect.
+     * @param {boolean} [isSorted] Specify the array is sorted.
+     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array} Returns the new duplicate-value-free array.
+     * @example
+     *
+     * _.uniq([2, 1, 2]);
+     * // => [2, 1]
+     *
+     * // using `isSorted`
+     * _.uniq([1, 1, 2], true);
+     * // => [1, 2]
+     *
+     * // using an iteratee function
+     * _.uniq([1, 2.5, 1.5, 2], function(n) {
+     *   return this.floor(n);
+     * }, Math);
+     * // => [1, 2.5]
+     *
+     * // using the `_.property` callback shorthand
+     * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 1 }, { 'x': 2 }]
+     */
+    function uniq(array, isSorted, iteratee, thisArg) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      if (isSorted != null && typeof isSorted != 'boolean') {
+        thisArg = iteratee;
+        iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted;
+        isSorted = false;
+      }
+      var callback = getCallback();
+      if (!(iteratee == null && callback === baseCallback)) {
+        iteratee = callback(iteratee, thisArg, 3);
+      }
+      return (isSorted && getIndexOf() === baseIndexOf)
+        ? sortedUniq(array, iteratee)
+        : baseUniq(array, iteratee);
+    }
+
+    /**
+     * This method is like `_.zip` except that it accepts an array of grouped
+     * elements and creates an array regrouping the elements to their pre-zip
+     * configuration.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array of grouped elements to process.
+     * @returns {Array} Returns the new array of regrouped elements.
+     * @example
+     *
+     * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);
+     * // => [['fred', 30, true], ['barney', 40, false]]
+     *
+     * _.unzip(zipped);
+     * // => [['fred', 'barney'], [30, 40], [true, false]]
+     */
+    function unzip(array) {
+      if (!(array && array.length)) {
+        return [];
+      }
+      var index = -1,
+          length = 0;
+
+      array = arrayFilter(array, function(group) {
+        if (isArrayLike(group)) {
+          length = nativeMax(group.length, length);
+          return true;
+        }
+      });
+      var result = Array(length);
+      while (++index < length) {
+        result[index] = arrayMap(array, baseProperty(index));
+      }
+      return result;
+    }
+
+    /**
+     * This method is like `_.unzip` except that it accepts an iteratee to specify
+     * how regrouped values should be combined. The `iteratee` is bound to `thisArg`
+     * and invoked with four arguments: (accumulator, value, index, group).
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array of grouped elements to process.
+     * @param {Function} [iteratee] The function to combine regrouped values.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array} Returns the new array of regrouped elements.
+     * @example
+     *
+     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
+     * // => [[1, 10, 100], [2, 20, 200]]
+     *
+     * _.unzipWith(zipped, _.add);
+     * // => [3, 30, 300]
+     */
+    function unzipWith(array, iteratee, thisArg) {
+      var length = array ? array.length : 0;
+      if (!length) {
+        return [];
+      }
+      var result = unzip(array);
+      if (iteratee == null) {
+        return result;
+      }
+      iteratee = bindCallback(iteratee, thisArg, 4);
+      return arrayMap(result, function(group) {
+        return arrayReduce(group, iteratee, undefined, true);
+      });
+    }
+
+    /**
+     * Creates an array excluding all provided values using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {Array} array The array to filter.
+     * @param {...*} [values] The values to exclude.
+     * @returns {Array} Returns the new array of filtered values.
+     * @example
+     *
+     * _.without([1, 2, 1, 3], 1, 2);
+     * // => [3]
+     */
+    var without = restParam(function(array, values) {
+      return isArrayLike(array)
+        ? baseDifference(array, values)
+        : [];
+    });
+
+    /**
+     * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
+     * of the provided arrays.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {...Array} [arrays] The arrays to inspect.
+     * @returns {Array} Returns the new array of values.
+     * @example
+     *
+     * _.xor([1, 2], [4, 2]);
+     * // => [1, 4]
+     */
+    function xor() {
+      var index = -1,
+          length = arguments.length;
+
+      while (++index < length) {
+        var array = arguments[index];
+        if (isArrayLike(array)) {
+          var result = result
+            ? arrayPush(baseDifference(result, array), baseDifference(array, result))
+            : array;
+        }
+      }
+      return result ? baseUniq(result) : [];
+    }
+
+    /**
+     * Creates an array of grouped elements, the first of which contains the first
+     * elements of the given arrays, the second of which contains the second elements
+     * of the given arrays, and so on.
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {...Array} [arrays] The arrays to process.
+     * @returns {Array} Returns the new array of grouped elements.
+     * @example
+     *
+     * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+     * // => [['fred', 30, true], ['barney', 40, false]]
+     */
+    var zip = restParam(unzip);
+
+    /**
+     * The inverse of `_.pairs`; this method returns an object composed from arrays
+     * of property names and values. Provide either a single two dimensional array,
+     * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names
+     * and one of corresponding values.
+     *
+     * @static
+     * @memberOf _
+     * @alias object
+     * @category Array
+     * @param {Array} props The property names.
+     * @param {Array} [values=[]] The property values.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * _.zipObject([['fred', 30], ['barney', 40]]);
+     * // => { 'fred': 30, 'barney': 40 }
+     *
+     * _.zipObject(['fred', 'barney'], [30, 40]);
+     * // => { 'fred': 30, 'barney': 40 }
+     */
+    function zipObject(props, values) {
+      var index = -1,
+          length = props ? props.length : 0,
+          result = {};
+
+      if (length && !values && !isArray(props[0])) {
+        values = [];
+      }
+      while (++index < length) {
+        var key = props[index];
+        if (values) {
+          result[key] = values[index];
+        } else if (key) {
+          result[key[0]] = key[1];
+        }
+      }
+      return result;
+    }
+
+    /**
+     * This method is like `_.zip` except that it accepts an iteratee to specify
+     * how grouped values should be combined. The `iteratee` is bound to `thisArg`
+     * and invoked with four arguments: (accumulator, value, index, group).
+     *
+     * @static
+     * @memberOf _
+     * @category Array
+     * @param {...Array} [arrays] The arrays to process.
+     * @param {Function} [iteratee] The function to combine grouped values.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array} Returns the new array of grouped elements.
+     * @example
+     *
+     * _.zipWith([1, 2], [10, 20], [100, 200], _.add);
+     * // => [111, 222]
+     */
+    var zipWith = restParam(function(arrays) {
+      var length = arrays.length,
+          iteratee = length > 2 ? arrays[length - 2] : undefined,
+          thisArg = length > 1 ? arrays[length - 1] : undefined;
+
+      if (length > 2 && typeof iteratee == 'function') {
+        length -= 2;
+      } else {
+        iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined;
+        thisArg = undefined;
+      }
+      arrays.length = length;
+      return unzipWith(arrays, iteratee, thisArg);
+    });
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object that wraps `value` with explicit method
+     * chaining enabled.
+     *
+     * @static
+     * @memberOf _
+     * @category Chain
+     * @param {*} value The value to wrap.
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'age': 36 },
+     *   { 'user': 'fred',    'age': 40 },
+     *   { 'user': 'pebbles', 'age': 1 }
+     * ];
+     *
+     * var youngest = _.chain(users)
+     *   .sortBy('age')
+     *   .map(function(chr) {
+     *     return chr.user + ' is ' + chr.age;
+     *   })
+     *   .first()
+     *   .value();
+     * // => 'pebbles is 1'
+     */
+    function chain(value) {
+      var result = lodash(value);
+      result.__chain__ = true;
+      return result;
+    }
+
+    /**
+     * This method invokes `interceptor` and returns `value`. The interceptor is
+     * bound to `thisArg` and invoked with one argument; (value). The purpose of
+     * this method is to "tap into" a method chain in order to perform operations
+     * on intermediate results within the chain.
+     *
+     * @static
+     * @memberOf _
+     * @category Chain
+     * @param {*} value The value to provide to `interceptor`.
+     * @param {Function} interceptor The function to invoke.
+     * @param {*} [thisArg] The `this` binding of `interceptor`.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * _([1, 2, 3])
+     *  .tap(function(array) {
+     *    array.pop();
+     *  })
+     *  .reverse()
+     *  .value();
+     * // => [2, 1]
+     */
+    function tap(value, interceptor, thisArg) {
+      interceptor.call(thisArg, value);
+      return value;
+    }
+
+    /**
+     * This method is like `_.tap` except that it returns the result of `interceptor`.
+     *
+     * @static
+     * @memberOf _
+     * @category Chain
+     * @param {*} value The value to provide to `interceptor`.
+     * @param {Function} interceptor The function to invoke.
+     * @param {*} [thisArg] The `this` binding of `interceptor`.
+     * @returns {*} Returns the result of `interceptor`.
+     * @example
+     *
+     * _('  abc  ')
+     *  .chain()
+     *  .trim()
+     *  .thru(function(value) {
+     *    return [value];
+     *  })
+     *  .value();
+     * // => ['abc']
+     */
+    function thru(value, interceptor, thisArg) {
+      return interceptor.call(thisArg, value);
+    }
+
+    /**
+     * Enables explicit method chaining on the wrapper object.
+     *
+     * @name chain
+     * @memberOf _
+     * @category Chain
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 }
+     * ];
+     *
+     * // without explicit chaining
+     * _(users).first();
+     * // => { 'user': 'barney', 'age': 36 }
+     *
+     * // with explicit chaining
+     * _(users).chain()
+     *   .first()
+     *   .pick('user')
+     *   .value();
+     * // => { 'user': 'barney' }
+     */
+    function wrapperChain() {
+      return chain(this);
+    }
+
+    /**
+     * Executes the chained sequence and returns the wrapped result.
+     *
+     * @name commit
+     * @memberOf _
+     * @category Chain
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var array = [1, 2];
+     * var wrapped = _(array).push(3);
+     *
+     * console.log(array);
+     * // => [1, 2]
+     *
+     * wrapped = wrapped.commit();
+     * console.log(array);
+     * // => [1, 2, 3]
+     *
+     * wrapped.last();
+     * // => 3
+     *
+     * console.log(array);
+     * // => [1, 2, 3]
+     */
+    function wrapperCommit() {
+      return new LodashWrapper(this.value(), this.__chain__);
+    }
+
+    /**
+     * Creates a new array joining a wrapped array with any additional arrays
+     * and/or values.
+     *
+     * @name concat
+     * @memberOf _
+     * @category Chain
+     * @param {...*} [values] The values to concatenate.
+     * @returns {Array} Returns the new concatenated array.
+     * @example
+     *
+     * var array = [1];
+     * var wrapped = _(array).concat(2, [3], [[4]]);
+     *
+     * console.log(wrapped.value());
+     * // => [1, 2, 3, [4]]
+     *
+     * console.log(array);
+     * // => [1]
+     */
+    var wrapperConcat = restParam(function(values) {
+      values = baseFlatten(values);
+      return this.thru(function(array) {
+        return arrayConcat(isArray(array) ? array : [toObject(array)], values);
+      });
+    });
+
+    /**
+     * Creates a clone of the chained sequence planting `value` as the wrapped value.
+     *
+     * @name plant
+     * @memberOf _
+     * @category Chain
+     * @returns {Object} Returns the new `lodash` wrapper instance.
+     * @example
+     *
+     * var array = [1, 2];
+     * var wrapped = _(array).map(function(value) {
+     *   return Math.pow(value, 2);
+     * });
+     *
+     * var other = [3, 4];
+     * var otherWrapped = wrapped.plant(other);
+     *
+     * otherWrapped.value();
+     * // => [9, 16]
+     *
+     * wrapped.value();
+     * // => [1, 4]
+     */
+    function wrapperPlant(value) {
+      var result,
+          parent = this;
+
+      while (parent instanceof baseLodash) {
+        var clone = wrapperClone(parent);
+        if (result) {
+          previous.__wrapped__ = clone;
+        } else {
+          result = clone;
+        }
+        var previous = clone;
+        parent = parent.__wrapped__;
+      }
+      previous.__wrapped__ = value;
+      return result;
+    }
+
+    /**
+     * Reverses the wrapped array so the first element becomes the last, the
+     * second element becomes the second to last, and so on.
+     *
+     * **Note:** This method mutates the wrapped array.
+     *
+     * @name reverse
+     * @memberOf _
+     * @category Chain
+     * @returns {Object} Returns the new reversed `lodash` wrapper instance.
+     * @example
+     *
+     * var array = [1, 2, 3];
+     *
+     * _(array).reverse().value()
+     * // => [3, 2, 1]
+     *
+     * console.log(array);
+     * // => [3, 2, 1]
+     */
+    function wrapperReverse() {
+      var value = this.__wrapped__;
+
+      var interceptor = function(value) {
+        return value.reverse();
+      };
+      if (value instanceof LazyWrapper) {
+        var wrapped = value;
+        if (this.__actions__.length) {
+          wrapped = new LazyWrapper(this);
+        }
+        wrapped = wrapped.reverse();
+        wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
+        return new LodashWrapper(wrapped, this.__chain__);
+      }
+      return this.thru(interceptor);
+    }
+
+    /**
+     * Produces the result of coercing the unwrapped value to a string.
+     *
+     * @name toString
+     * @memberOf _
+     * @category Chain
+     * @returns {string} Returns the coerced string value.
+     * @example
+     *
+     * _([1, 2, 3]).toString();
+     * // => '1,2,3'
+     */
+    function wrapperToString() {
+      return (this.value() + '');
+    }
+
+    /**
+     * Executes the chained sequence to extract the unwrapped value.
+     *
+     * @name value
+     * @memberOf _
+     * @alias run, toJSON, valueOf
+     * @category Chain
+     * @returns {*} Returns the resolved unwrapped value.
+     * @example
+     *
+     * _([1, 2, 3]).value();
+     * // => [1, 2, 3]
+     */
+    function wrapperValue() {
+      return baseWrapperValue(this.__wrapped__, this.__actions__);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array of elements corresponding to the given keys, or indexes,
+     * of `collection`. Keys may be specified as individual arguments or as arrays
+     * of keys.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {...(number|number[]|string|string[])} [props] The property names
+     *  or indexes of elements to pick, specified individually or in arrays.
+     * @returns {Array} Returns the new array of picked elements.
+     * @example
+     *
+     * _.at(['a', 'b', 'c'], [0, 2]);
+     * // => ['a', 'c']
+     *
+     * _.at(['barney', 'fred', 'pebbles'], 0, 2);
+     * // => ['barney', 'pebbles']
+     */
+    var at = restParam(function(collection, props) {
+      return baseAt(collection, baseFlatten(props));
+    });
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` through `iteratee`. The corresponding value
+     * of each key is the number of times the key was returned by `iteratee`.
+     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.countBy([4.3, 6.1, 6.4], function(n) {
+     *   return Math.floor(n);
+     * });
+     * // => { '4': 1, '6': 2 }
+     *
+     * _.countBy([4.3, 6.1, 6.4], function(n) {
+     *   return this.floor(n);
+     * }, Math);
+     * // => { '4': 1, '6': 2 }
+     *
+     * _.countBy(['one', 'two', 'three'], 'length');
+     * // => { '3': 2, '5': 1 }
+     */
+    var countBy = createAggregator(function(result, value, key) {
+      hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1);
+    });
+
+    /**
+     * Checks if `predicate` returns truthy for **all** elements of `collection`.
+     * The predicate is bound to `thisArg` and invoked with three arguments:
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias all
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {boolean} Returns `true` if all elements pass the predicate check,
+     *  else `false`.
+     * @example
+     *
+     * _.every([true, 1, null, 'yes'], Boolean);
+     * // => false
+     *
+     * var users = [
+     *   { 'user': 'barney', 'active': false },
+     *   { 'user': 'fred',   'active': false }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.every(users, { 'user': 'barney', 'active': false });
+     * // => false
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.every(users, 'active', false);
+     * // => true
+     *
+     * // using the `_.property` callback shorthand
+     * _.every(users, 'active');
+     * // => false
+     */
+    function every(collection, predicate, thisArg) {
+      var func = isArray(collection) ? arrayEvery : baseEvery;
+      if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
+        predicate = undefined;
+      }
+      if (typeof predicate != 'function' || thisArg !== undefined) {
+        predicate = getCallback(predicate, thisArg, 3);
+      }
+      return func(collection, predicate);
+    }
+
+    /**
+     * Iterates over elements of `collection`, returning an array of all elements
+     * `predicate` returns truthy for. The predicate is bound to `thisArg` and
+     * invoked with three arguments: (value, index|key, collection).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias select
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the new filtered array.
+     * @example
+     *
+     * _.filter([4, 5, 6], function(n) {
+     *   return n % 2 == 0;
+     * });
+     * // => [4, 6]
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': true },
+     *   { 'user': 'fred',   'age': 40, 'active': false }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user');
+     * // => ['barney']
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.pluck(_.filter(users, 'active', false), 'user');
+     * // => ['fred']
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.filter(users, 'active'), 'user');
+     * // => ['barney']
+     */
+    function filter(collection, predicate, thisArg) {
+      var func = isArray(collection) ? arrayFilter : baseFilter;
+      predicate = getCallback(predicate, thisArg, 3);
+      return func(collection, predicate);
+    }
+
+    /**
+     * Iterates over elements of `collection`, returning the first element
+     * `predicate` returns truthy for. The predicate is bound to `thisArg` and
+     * invoked with three arguments: (value, index|key, collection).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias detect
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {*} Returns the matched element, else `undefined`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney',  'age': 36, 'active': true },
+     *   { 'user': 'fred',    'age': 40, 'active': false },
+     *   { 'user': 'pebbles', 'age': 1,  'active': true }
+     * ];
+     *
+     * _.result(_.find(users, function(chr) {
+     *   return chr.age < 40;
+     * }), 'user');
+     * // => 'barney'
+     *
+     * // using the `_.matches` callback shorthand
+     * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
+     * // => 'pebbles'
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.result(_.find(users, 'active', false), 'user');
+     * // => 'fred'
+     *
+     * // using the `_.property` callback shorthand
+     * _.result(_.find(users, 'active'), 'user');
+     * // => 'barney'
+     */
+    var find = createFind(baseEach);
+
+    /**
+     * This method is like `_.find` except that it iterates over elements of
+     * `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {*} Returns the matched element, else `undefined`.
+     * @example
+     *
+     * _.findLast([1, 2, 3, 4], function(n) {
+     *   return n % 2 == 1;
+     * });
+     * // => 3
+     */
+    var findLast = createFind(baseEachRight, true);
+
+    /**
+     * Performs a deep comparison between each element in `collection` and the
+     * source object, returning the first element that has equivalent property
+     * values.
+     *
+     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+     * numbers, `Object` objects, regexes, and strings. Objects are compared by
+     * their own, not inherited, enumerable properties. For comparing a single
+     * own or inherited property value see `_.matchesProperty`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {Object} source The object of property values to match.
+     * @returns {*} Returns the matched element, else `undefined`.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': true },
+     *   { 'user': 'fred',   'age': 40, 'active': false }
+     * ];
+     *
+     * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user');
+     * // => 'barney'
+     *
+     * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user');
+     * // => 'fred'
+     */
+    function findWhere(collection, source) {
+      return find(collection, baseMatches(source));
+    }
+
+    /**
+     * Iterates over elements of `collection` invoking `iteratee` for each element.
+     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
+     * (value, index|key, collection). Iteratee functions may exit iteration early
+     * by explicitly returning `false`.
+     *
+     * **Note:** As with other "Collections" methods, objects with a "length" property
+     * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+     * may be used for object iteration.
+     *
+     * @static
+     * @memberOf _
+     * @alias each
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array|Object|string} Returns `collection`.
+     * @example
+     *
+     * _([1, 2]).forEach(function(n) {
+     *   console.log(n);
+     * }).value();
+     * // => logs each value from left to right and returns the array
+     *
+     * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
+     *   console.log(n, key);
+     * });
+     * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
+     */
+    var forEach = createForEach(arrayEach, baseEach);
+
+    /**
+     * This method is like `_.forEach` except that it iterates over elements of
+     * `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias eachRight
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array|Object|string} Returns `collection`.
+     * @example
+     *
+     * _([1, 2]).forEachRight(function(n) {
+     *   console.log(n);
+     * }).value();
+     * // => logs each value from right to left and returns the array
+     */
+    var forEachRight = createForEach(arrayEachRight, baseEachRight);
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` through `iteratee`. The corresponding value
+     * of each key is an array of the elements responsible for generating the key.
+     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.groupBy([4.2, 6.1, 6.4], function(n) {
+     *   return Math.floor(n);
+     * });
+     * // => { '4': [4.2], '6': [6.1, 6.4] }
+     *
+     * _.groupBy([4.2, 6.1, 6.4], function(n) {
+     *   return this.floor(n);
+     * }, Math);
+     * // => { '4': [4.2], '6': [6.1, 6.4] }
+     *
+     * // using the `_.property` callback shorthand
+     * _.groupBy(['one', 'two', 'three'], 'length');
+     * // => { '3': ['one', 'two'], '5': ['three'] }
+     */
+    var groupBy = createAggregator(function(result, value, key) {
+      if (hasOwnProperty.call(result, key)) {
+        result[key].push(value);
+      } else {
+        result[key] = [value];
+      }
+    });
+
+    /**
+     * Checks if `target` is in `collection` using
+     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+     * for equality comparisons. If `fromIndex` is negative, it's used as the offset
+     * from the end of `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @alias contains, include
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {*} target The value to search for.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
+     * @returns {boolean} Returns `true` if a matching element is found, else `false`.
+     * @example
+     *
+     * _.includes([1, 2, 3], 1);
+     * // => true
+     *
+     * _.includes([1, 2, 3], 1, 2);
+     * // => false
+     *
+     * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');
+     * // => true
+     *
+     * _.includes('pebbles', 'eb');
+     * // => true
+     */
+    function includes(collection, target, fromIndex, guard) {
+      var length = collection ? getLength(collection) : 0;
+      if (!isLength(length)) {
+        collection = values(collection);
+        length = collection.length;
+      }
+      if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) {
+        fromIndex = 0;
+      } else {
+        fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
+      }
+      return (typeof collection == 'string' || !isArray(collection) && isString(collection))
+        ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1)
+        : (!!length && getIndexOf(collection, target, fromIndex) > -1);
+    }
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` through `iteratee`. The corresponding value
+     * of each key is the last element responsible for generating the key. The
+     * iteratee function is bound to `thisArg` and invoked with three arguments:
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * var keyData = [
+     *   { 'dir': 'left', 'code': 97 },
+     *   { 'dir': 'right', 'code': 100 }
+     * ];
+     *
+     * _.indexBy(keyData, 'dir');
+     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.indexBy(keyData, function(object) {
+     *   return String.fromCharCode(object.code);
+     * });
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.indexBy(keyData, function(object) {
+     *   return this.fromCharCode(object.code);
+     * }, String);
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     */
+    var indexBy = createAggregator(function(result, value, key) {
+      result[key] = value;
+    });
+
+    /**
+     * Invokes the method at `path` of each element in `collection`, returning
+     * an array of the results of each invoked method. Any additional arguments
+     * are provided to each invoked method. If `methodName` is a function it's
+     * invoked for, and `this` bound to, each element in `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Array|Function|string} path The path of the method to invoke or
+     *  the function invoked per iteration.
+     * @param {...*} [args] The arguments to invoke the method with.
+     * @returns {Array} Returns the array of results.
+     * @example
+     *
+     * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+     * // => [[1, 5, 7], [1, 2, 3]]
+     *
+     * _.invoke([123, 456], String.prototype.split, '');
+     * // => [['1', '2', '3'], ['4', '5', '6']]
+     */
+    var invoke = restParam(function(collection, path, args) {
+      var index = -1,
+          isFunc = typeof path == 'function',
+          isProp = isKey(path),
+          result = isArrayLike(collection) ? Array(collection.length) : [];
+
+      baseEach(collection, function(value) {
+        var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined);
+        result[++index] = func ? func.apply(value, args) : invokePath(value, path, args);
+      });
+      return result;
+    });
+
+    /**
+     * Creates an array of values by running each element in `collection` through
+     * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
+     * arguments: (value, index|key, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * Many lodash methods are guarded to work as iteratees for methods like
+     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+     *
+     * The guarded methods are:
+     * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`,
+     * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`,
+     * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`,
+     * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`,
+     * `sum`, `uniq`, and `words`
+     *
+     * @static
+     * @memberOf _
+     * @alias collect
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array} Returns the new mapped array.
+     * @example
+     *
+     * function timesThree(n) {
+     *   return n * 3;
+     * }
+     *
+     * _.map([1, 2], timesThree);
+     * // => [3, 6]
+     *
+     * _.map({ 'a': 1, 'b': 2 }, timesThree);
+     * // => [3, 6] (iteration order is not guaranteed)
+     *
+     * var users = [
+     *   { 'user': 'barney' },
+     *   { 'user': 'fred' }
+     * ];
+     *
+     * // using the `_.property` callback shorthand
+     * _.map(users, 'user');
+     * // => ['barney', 'fred']
+     */
+    function map(collection, iteratee, thisArg) {
+      var func = isArray(collection) ? arrayMap : baseMap;
+      iteratee = getCallback(iteratee, thisArg, 3);
+      return func(collection, iteratee);
+    }
+
+    /**
+     * Creates an array of elements split into two groups, the first of which
+     * contains elements `predicate` returns truthy for, while the second of which
+     * contains elements `predicate` returns falsey for. The predicate is bound
+     * to `thisArg` and invoked with three arguments: (value, index|key, collection).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the array of grouped elements.
+     * @example
+     *
+     * _.partition([1, 2, 3], function(n) {
+     *   return n % 2;
+     * });
+     * // => [[1, 3], [2]]
+     *
+     * _.partition([1.2, 2.3, 3.4], function(n) {
+     *   return this.floor(n) % 2;
+     * }, Math);
+     * // => [[1.2, 3.4], [2.3]]
+     *
+     * var users = [
+     *   { 'user': 'barney',  'age': 36, 'active': false },
+     *   { 'user': 'fred',    'age': 40, 'active': true },
+     *   { 'user': 'pebbles', 'age': 1,  'active': false }
+     * ];
+     *
+     * var mapper = function(array) {
+     *   return _.pluck(array, 'user');
+     * };
+     *
+     * // using the `_.matches` callback shorthand
+     * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper);
+     * // => [['pebbles'], ['barney', 'fred']]
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.map(_.partition(users, 'active', false), mapper);
+     * // => [['barney', 'pebbles'], ['fred']]
+     *
+     * // using the `_.property` callback shorthand
+     * _.map(_.partition(users, 'active'), mapper);
+     * // => [['fred'], ['barney', 'pebbles']]
+     */
+    var partition = createAggregator(function(result, value, key) {
+      result[key ? 0 : 1].push(value);
+    }, function() { return [[], []]; });
+
+    /**
+     * Gets the property value of `path` from all elements in `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Array|string} path The path of the property to pluck.
+     * @returns {Array} Returns the property values.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.pluck(users, 'user');
+     * // => ['barney', 'fred']
+     *
+     * var userIndex = _.indexBy(users, 'user');
+     * _.pluck(userIndex, 'age');
+     * // => [36, 40] (iteration order is not guaranteed)
+     */
+    function pluck(collection, path) {
+      return map(collection, property(path));
+    }
+
+    /**
+     * Reduces `collection` to a value which is the accumulated result of running
+     * each element in `collection` through `iteratee`, where each successive
+     * invocation is supplied the return value of the previous. If `accumulator`
+     * is not provided the first element of `collection` is used as the initial
+     * value. The `iteratee` is bound to `thisArg` and invoked with four arguments:
+     * (accumulator, value, index|key, collection).
+     *
+     * Many lodash methods are guarded to work as iteratees for methods like
+     * `_.reduce`, `_.reduceRight`, and `_.transform`.
+     *
+     * The guarded methods are:
+     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`,
+     * and `sortByOrder`
+     *
+     * @static
+     * @memberOf _
+     * @alias foldl, inject
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [accumulator] The initial value.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * _.reduce([1, 2], function(total, n) {
+     *   return total + n;
+     * });
+     * // => 3
+     *
+     * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) {
+     *   result[key] = n * 3;
+     *   return result;
+     * }, {});
+     * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed)
+     */
+    var reduce = createReduce(arrayReduce, baseEach);
+
+    /**
+     * This method is like `_.reduce` except that it iterates over elements of
+     * `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias foldr
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [accumulator] The initial value.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var array = [[0, 1], [2, 3], [4, 5]];
+     *
+     * _.reduceRight(array, function(flattened, other) {
+     *   return flattened.concat(other);
+     * }, []);
+     * // => [4, 5, 2, 3, 0, 1]
+     */
+    var reduceRight = createReduce(arrayReduceRight, baseEachRight);
+
+    /**
+     * The opposite of `_.filter`; this method returns the elements of `collection`
+     * that `predicate` does **not** return truthy for.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Array} Returns the new filtered array.
+     * @example
+     *
+     * _.reject([1, 2, 3, 4], function(n) {
+     *   return n % 2 == 0;
+     * });
+     * // => [1, 3]
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': false },
+     *   { 'user': 'fred',   'age': 40, 'active': true }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user');
+     * // => ['barney']
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.pluck(_.reject(users, 'active', false), 'user');
+     * // => ['fred']
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.reject(users, 'active'), 'user');
+     * // => ['barney']
+     */
+    function reject(collection, predicate, thisArg) {
+      var func = isArray(collection) ? arrayFilter : baseFilter;
+      predicate = getCallback(predicate, thisArg, 3);
+      return func(collection, function(value, index, collection) {
+        return !predicate(value, index, collection);
+      });
+    }
+
+    /**
+     * Gets a random element or `n` random elements from a collection.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to sample.
+     * @param {number} [n] The number of elements to sample.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {*} Returns the random sample(s).
+     * @example
+     *
+     * _.sample([1, 2, 3, 4]);
+     * // => 2
+     *
+     * _.sample([1, 2, 3, 4], 2);
+     * // => [3, 1]
+     */
+    function sample(collection, n, guard) {
+      if (guard ? isIterateeCall(collection, n, guard) : n == null) {
+        collection = toIterable(collection);
+        var length = collection.length;
+        return length > 0 ? collection[baseRandom(0, length - 1)] : undefined;
+      }
+      var index = -1,
+          result = toArray(collection),
+          length = result.length,
+          lastIndex = length - 1;
+
+      n = nativeMin(n < 0 ? 0 : (+n || 0), length);
+      while (++index < n) {
+        var rand = baseRandom(index, lastIndex),
+            value = result[rand];
+
+        result[rand] = result[index];
+        result[index] = value;
+      }
+      result.length = n;
+      return result;
+    }
+
+    /**
+     * Creates an array of shuffled values, using a version of the
+     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to shuffle.
+     * @returns {Array} Returns the new shuffled array.
+     * @example
+     *
+     * _.shuffle([1, 2, 3, 4]);
+     * // => [4, 1, 3, 2]
+     */
+    function shuffle(collection) {
+      return sample(collection, POSITIVE_INFINITY);
+    }
+
+    /**
+     * Gets the size of `collection` by returning its length for array-like
+     * values or the number of own enumerable properties for objects.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to inspect.
+     * @returns {number} Returns the size of `collection`.
+     * @example
+     *
+     * _.size([1, 2, 3]);
+     * // => 3
+     *
+     * _.size({ 'a': 1, 'b': 2 });
+     * // => 2
+     *
+     * _.size('pebbles');
+     * // => 7
+     */
+    function size(collection) {
+      var length = collection ? getLength(collection) : 0;
+      return isLength(length) ? length : keys(collection).length;
+    }
+
+    /**
+     * Checks if `predicate` returns truthy for **any** element of `collection`.
+     * The function returns as soon as it finds a passing value and does not iterate
+     * over the entire collection. The predicate is bound to `thisArg` and invoked
+     * with three arguments: (value, index|key, collection).
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias any
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {boolean} Returns `true` if any element passes the predicate check,
+     *  else `false`.
+     * @example
+     *
+     * _.some([null, 0, 'yes', false], Boolean);
+     * // => true
+     *
+     * var users = [
+     *   { 'user': 'barney', 'active': true },
+     *   { 'user': 'fred',   'active': false }
+     * ];
+     *
+     * // using the `_.matches` callback shorthand
+     * _.some(users, { 'user': 'barney', 'active': false });
+     * // => false
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.some(users, 'active', false);
+     * // => true
+     *
+     * // using the `_.property` callback shorthand
+     * _.some(users, 'active');
+     * // => true
+     */
+    function some(collection, predicate, thisArg) {
+      var func = isArray(collection) ? arraySome : baseSome;
+      if (thisArg && isIterateeCall(collection, predicate, thisArg)) {
+        predicate = undefined;
+      }
+      if (typeof predicate != 'function' || thisArg !== undefined) {
+        predicate = getCallback(predicate, thisArg, 3);
+      }
+      return func(collection, predicate);
+    }
+
+    /**
+     * Creates an array of elements, sorted in ascending order by the results of
+     * running each element in a collection through `iteratee`. This method performs
+     * a stable sort, that is, it preserves the original sort order of equal elements.
+     * The `iteratee` is bound to `thisArg` and invoked with three arguments:
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array} Returns the new sorted array.
+     * @example
+     *
+     * _.sortBy([1, 2, 3], function(n) {
+     *   return Math.sin(n);
+     * });
+     * // => [3, 1, 2]
+     *
+     * _.sortBy([1, 2, 3], function(n) {
+     *   return this.sin(n);
+     * }, Math);
+     * // => [3, 1, 2]
+     *
+     * var users = [
+     *   { 'user': 'fred' },
+     *   { 'user': 'pebbles' },
+     *   { 'user': 'barney' }
+     * ];
+     *
+     * // using the `_.property` callback shorthand
+     * _.pluck(_.sortBy(users, 'user'), 'user');
+     * // => ['barney', 'fred', 'pebbles']
+     */
+    function sortBy(collection, iteratee, thisArg) {
+      if (collection == null) {
+        return [];
+      }
+      if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
+        iteratee = undefined;
+      }
+      var index = -1;
+      iteratee = getCallback(iteratee, thisArg, 3);
+
+      var result = baseMap(collection, function(value, key, collection) {
+        return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value };
+      });
+      return baseSortBy(result, compareAscending);
+    }
+
+    /**
+     * This method is like `_.sortBy` except that it can sort by multiple iteratees
+     * or property names.
+     *
+     * If a property name is provided for an iteratee the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If an object is provided for an iteratee the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees
+     *  The iteratees to sort by, specified as individual values or arrays of values.
+     * @returns {Array} Returns the new sorted array.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'fred',   'age': 48 },
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 42 },
+     *   { 'user': 'barney', 'age': 34 }
+     * ];
+     *
+     * _.map(_.sortByAll(users, ['user', 'age']), _.values);
+     * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]]
+     *
+     * _.map(_.sortByAll(users, 'user', function(chr) {
+     *   return Math.floor(chr.age / 10);
+     * }), _.values);
+     * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
+     */
+    var sortByAll = restParam(function(collection, iteratees) {
+      if (collection == null) {
+        return [];
+      }
+      var guard = iteratees[2];
+      if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) {
+        iteratees.length = 1;
+      }
+      return baseSortByOrder(collection, baseFlatten(iteratees), []);
+    });
+
+    /**
+     * This method is like `_.sortByAll` except that it allows specifying the
+     * sort orders of the iteratees to sort by. If `orders` is unspecified, all
+     * values are sorted in ascending order. Otherwise, a value is sorted in
+     * ascending order if its corresponding order is "asc", and descending if "desc".
+     *
+     * If a property name is provided for an iteratee the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If an object is provided for an iteratee the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
+     * @param {boolean[]} [orders] The sort orders of `iteratees`.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
+     * @returns {Array} Returns the new sorted array.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'fred',   'age': 48 },
+     *   { 'user': 'barney', 'age': 34 },
+     *   { 'user': 'fred',   'age': 42 },
+     *   { 'user': 'barney', 'age': 36 }
+     * ];
+     *
+     * // sort by `user` in ascending order and by `age` in descending order
+     * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values);
+     * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
+     */
+    function sortByOrder(collection, iteratees, orders, guard) {
+      if (collection == null) {
+        return [];
+      }
+      if (guard && isIterateeCall(iteratees, orders, guard)) {
+        orders = undefined;
+      }
+      if (!isArray(iteratees)) {
+        iteratees = iteratees == null ? [] : [iteratees];
+      }
+      if (!isArray(orders)) {
+        orders = orders == null ? [] : [orders];
+      }
+      return baseSortByOrder(collection, iteratees, orders);
+    }
+
+    /**
+     * Performs a deep comparison between each element in `collection` and the
+     * source object, returning an array of all elements that have equivalent
+     * property values.
+     *
+     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+     * numbers, `Object` objects, regexes, and strings. Objects are compared by
+     * their own, not inherited, enumerable properties. For comparing a single
+     * own or inherited property value see `_.matchesProperty`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collection
+     * @param {Array|Object|string} collection The collection to search.
+     * @param {Object} source The object of property values to match.
+     * @returns {Array} Returns the new filtered array.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] },
+     *   { 'user': 'fred',   'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] }
+     * ];
+     *
+     * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user');
+     * // => ['barney']
+     *
+     * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user');
+     * // => ['fred']
+     */
+    function where(collection, source) {
+      return filter(collection, baseMatches(source));
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Gets the number of milliseconds that have elapsed since the Unix epoch
+     * (1 January 1970 00:00:00 UTC).
+     *
+     * @static
+     * @memberOf _
+     * @category Date
+     * @example
+     *
+     * _.defer(function(stamp) {
+     *   console.log(_.now() - stamp);
+     * }, _.now());
+     * // => logs the number of milliseconds it took for the deferred function to be invoked
+     */
+    var now = nativeNow || function() {
+      return new Date().getTime();
+    };
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The opposite of `_.before`; this method creates a function that invokes
+     * `func` once it's called `n` or more times.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {number} n The number of calls before `func` is invoked.
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var saves = ['profile', 'settings'];
+     *
+     * var done = _.after(saves.length, function() {
+     *   console.log('done saving!');
+     * });
+     *
+     * _.forEach(saves, function(type) {
+     *   asyncSave({ 'type': type, 'complete': done });
+     * });
+     * // => logs 'done saving!' after the two async saves have completed
+     */
+    function after(n, func) {
+      if (typeof func != 'function') {
+        if (typeof n == 'function') {
+          var temp = n;
+          n = func;
+          func = temp;
+        } else {
+          throw new TypeError(FUNC_ERROR_TEXT);
+        }
+      }
+      n = nativeIsFinite(n = +n) ? n : 0;
+      return function() {
+        if (--n < 1) {
+          return func.apply(this, arguments);
+        }
+      };
+    }
+
+    /**
+     * Creates a function that accepts up to `n` arguments ignoring any
+     * additional arguments.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to cap arguments for.
+     * @param {number} [n=func.length] The arity cap.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * _.map(['6', '8', '10'], _.ary(parseInt, 1));
+     * // => [6, 8, 10]
+     */
+    function ary(func, n, guard) {
+      if (guard && isIterateeCall(func, n, guard)) {
+        n = undefined;
+      }
+      n = (func && n == null) ? func.length : nativeMax(+n || 0, 0);
+      return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n);
+    }
+
+    /**
+     * Creates a function that invokes `func`, with the `this` binding and arguments
+     * of the created function, while it's called less than `n` times. Subsequent
+     * calls to the created function return the result of the last `func` invocation.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {number} n The number of calls at which `func` is no longer invoked.
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * jQuery('#add').on('click', _.before(5, addContactToList));
+     * // => allows adding up to 4 contacts to the list
+     */
+    function before(n, func) {
+      var result;
+      if (typeof func != 'function') {
+        if (typeof n == 'function') {
+          var temp = n;
+          n = func;
+          func = temp;
+        } else {
+          throw new TypeError(FUNC_ERROR_TEXT);
+        }
+      }
+      return function() {
+        if (--n > 0) {
+          result = func.apply(this, arguments);
+        }
+        if (n <= 1) {
+          func = undefined;
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of `thisArg`
+     * and prepends any additional `_.bind` arguments to those provided to the
+     * bound function.
+     *
+     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+     * may be used as a placeholder for partially applied arguments.
+     *
+     * **Note:** Unlike native `Function#bind` this method does not set the "length"
+     * property of bound functions.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to bind.
+     * @param {*} thisArg The `this` binding of `func`.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var greet = function(greeting, punctuation) {
+     *   return greeting + ' ' + this.user + punctuation;
+     * };
+     *
+     * var object = { 'user': 'fred' };
+     *
+     * var bound = _.bind(greet, object, 'hi');
+     * bound('!');
+     * // => 'hi fred!'
+     *
+     * // using placeholders
+     * var bound = _.bind(greet, object, _, '!');
+     * bound('hi');
+     * // => 'hi fred!'
+     */
+    var bind = restParam(function(func, thisArg, partials) {
+      var bitmask = BIND_FLAG;
+      if (partials.length) {
+        var holders = replaceHolders(partials, bind.placeholder);
+        bitmask |= PARTIAL_FLAG;
+      }
+      return createWrapper(func, bitmask, thisArg, partials, holders);
+    });
+
+    /**
+     * Binds methods of an object to the object itself, overwriting the existing
+     * method. Method names may be specified as individual arguments or as arrays
+     * of method names. If no method names are provided all enumerable function
+     * properties, own and inherited, of `object` are bound.
+     *
+     * **Note:** This method does not set the "length" property of bound functions.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Object} object The object to bind and assign the bound methods to.
+     * @param {...(string|string[])} [methodNames] The object method names to bind,
+     *  specified as individual method names or arrays of method names.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var view = {
+     *   'label': 'docs',
+     *   'onClick': function() {
+     *     console.log('clicked ' + this.label);
+     *   }
+     * };
+     *
+     * _.bindAll(view);
+     * jQuery('#docs').on('click', view.onClick);
+     * // => logs 'clicked docs' when the element is clicked
+     */
+    var bindAll = restParam(function(object, methodNames) {
+      methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object);
+
+      var index = -1,
+          length = methodNames.length;
+
+      while (++index < length) {
+        var key = methodNames[index];
+        object[key] = createWrapper(object[key], BIND_FLAG, object);
+      }
+      return object;
+    });
+
+    /**
+     * Creates a function that invokes the method at `object[key]` and prepends
+     * any additional `_.bindKey` arguments to those provided to the bound function.
+     *
+     * This method differs from `_.bind` by allowing bound functions to reference
+     * methods that may be redefined or don't yet exist.
+     * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
+     * for more details.
+     *
+     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for partially applied arguments.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Object} object The object the method belongs to.
+     * @param {string} key The key of the method.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var object = {
+     *   'user': 'fred',
+     *   'greet': function(greeting, punctuation) {
+     *     return greeting + ' ' + this.user + punctuation;
+     *   }
+     * };
+     *
+     * var bound = _.bindKey(object, 'greet', 'hi');
+     * bound('!');
+     * // => 'hi fred!'
+     *
+     * object.greet = function(greeting, punctuation) {
+     *   return greeting + 'ya ' + this.user + punctuation;
+     * };
+     *
+     * bound('!');
+     * // => 'hiya fred!'
+     *
+     * // using placeholders
+     * var bound = _.bindKey(object, 'greet', _, '!');
+     * bound('hi');
+     * // => 'hiya fred!'
+     */
+    var bindKey = restParam(function(object, key, partials) {
+      var bitmask = BIND_FLAG | BIND_KEY_FLAG;
+      if (partials.length) {
+        var holders = replaceHolders(partials, bindKey.placeholder);
+        bitmask |= PARTIAL_FLAG;
+      }
+      return createWrapper(key, bitmask, object, partials, holders);
+    });
+
+    /**
+     * Creates a function that accepts one or more arguments of `func` that when
+     * called either invokes `func` returning its result, if all `func` arguments
+     * have been provided, or returns a function that accepts one or more of the
+     * remaining `func` arguments, and so on. The arity of `func` may be specified
+     * if `func.length` is not sufficient.
+     *
+     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
+     * may be used as a placeholder for provided arguments.
+     *
+     * **Note:** This method does not set the "length" property of curried functions.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to curry.
+     * @param {number} [arity=func.length] The arity of `func`.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Function} Returns the new curried function.
+     * @example
+     *
+     * var abc = function(a, b, c) {
+     *   return [a, b, c];
+     * };
+     *
+     * var curried = _.curry(abc);
+     *
+     * curried(1)(2)(3);
+     * // => [1, 2, 3]
+     *
+     * curried(1, 2)(3);
+     * // => [1, 2, 3]
+     *
+     * curried(1, 2, 3);
+     * // => [1, 2, 3]
+     *
+     * // using placeholders
+     * curried(1)(_, 3)(2);
+     * // => [1, 2, 3]
+     */
+    var curry = createCurry(CURRY_FLAG);
+
+    /**
+     * This method is like `_.curry` except that arguments are applied to `func`
+     * in the manner of `_.partialRight` instead of `_.partial`.
+     *
+     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for provided arguments.
+     *
+     * **Note:** This method does not set the "length" property of curried functions.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to curry.
+     * @param {number} [arity=func.length] The arity of `func`.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Function} Returns the new curried function.
+     * @example
+     *
+     * var abc = function(a, b, c) {
+     *   return [a, b, c];
+     * };
+     *
+     * var curried = _.curryRight(abc);
+     *
+     * curried(3)(2)(1);
+     * // => [1, 2, 3]
+     *
+     * curried(2, 3)(1);
+     * // => [1, 2, 3]
+     *
+     * curried(1, 2, 3);
+     * // => [1, 2, 3]
+     *
+     * // using placeholders
+     * curried(3)(1, _)(2);
+     * // => [1, 2, 3]
+     */
+    var curryRight = createCurry(CURRY_RIGHT_FLAG);
+
+    /**
+     * Creates a debounced function that delays invoking `func` until after `wait`
+     * milliseconds have elapsed since the last time the debounced function was
+     * invoked. The debounced function comes with a `cancel` method to cancel
+     * delayed invocations. Provide an options object to indicate that `func`
+     * should be invoked on the leading and/or trailing edge of the `wait` timeout.
+     * Subsequent calls to the debounced function return the result of the last
+     * `func` invocation.
+     *
+     * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
+     * on the trailing edge of the timeout only if the the debounced function is
+     * invoked more than once during the `wait` timeout.
+     *
+     * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
+     * for details over the differences between `_.debounce` and `_.throttle`.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to debounce.
+     * @param {number} [wait=0] The number of milliseconds to delay.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.leading=false] Specify invoking on the leading
+     *  edge of the timeout.
+     * @param {number} [options.maxWait] The maximum time `func` is allowed to be
+     *  delayed before it's invoked.
+     * @param {boolean} [options.trailing=true] Specify invoking on the trailing
+     *  edge of the timeout.
+     * @returns {Function} Returns the new debounced function.
+     * @example
+     *
+     * // avoid costly calculations while the window size is in flux
+     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+     *
+     * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
+     * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+     *   'leading': true,
+     *   'trailing': false
+     * }));
+     *
+     * // ensure `batchLog` is invoked once after 1 second of debounced calls
+     * var source = new EventSource('/stream');
+     * jQuery(source).on('message', _.debounce(batchLog, 250, {
+     *   'maxWait': 1000
+     * }));
+     *
+     * // cancel a debounced call
+     * var todoChanges = _.debounce(batchLog, 1000);
+     * Object.observe(models.todo, todoChanges);
+     *
+     * Object.observe(models, function(changes) {
+     *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
+     *     todoChanges.cancel();
+     *   }
+     * }, ['delete']);
+     *
+     * // ...at some point `models.todo` is changed
+     * models.todo.completed = true;
+     *
+     * // ...before 1 second has passed `models.todo` is deleted
+     * // which cancels the debounced `todoChanges` call
+     * delete models.todo;
+     */
+    function debounce(func, wait, options) {
+      var args,
+          maxTimeoutId,
+          result,
+          stamp,
+          thisArg,
+          timeoutId,
+          trailingCall,
+          lastCalled = 0,
+          maxWait = false,
+          trailing = true;
+
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      wait = wait < 0 ? 0 : (+wait || 0);
+      if (options === true) {
+        var leading = true;
+        trailing = false;
+      } else if (isObject(options)) {
+        leading = !!options.leading;
+        maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
+        trailing = 'trailing' in options ? !!options.trailing : trailing;
+      }
+
+      function cancel() {
+        if (timeoutId) {
+          clearTimeout(timeoutId);
+        }
+        if (maxTimeoutId) {
+          clearTimeout(maxTimeoutId);
+        }
+        lastCalled = 0;
+        maxTimeoutId = timeoutId = trailingCall = undefined;
+      }
+
+      function complete(isCalled, id) {
+        if (id) {
+          clearTimeout(id);
+        }
+        maxTimeoutId = timeoutId = trailingCall = undefined;
+        if (isCalled) {
+          lastCalled = now();
+          result = func.apply(thisArg, args);
+          if (!timeoutId && !maxTimeoutId) {
+            args = thisArg = undefined;
+          }
+        }
+      }
+
+      function delayed() {
+        var remaining = wait - (now() - stamp);
+        if (remaining <= 0 || remaining > wait) {
+          complete(trailingCall, maxTimeoutId);
+        } else {
+          timeoutId = setTimeout(delayed, remaining);
+        }
+      }
+
+      function maxDelayed() {
+        complete(trailing, timeoutId);
+      }
+
+      function debounced() {
+        args = arguments;
+        stamp = now();
+        thisArg = this;
+        trailingCall = trailing && (timeoutId || !leading);
+
+        if (maxWait === false) {
+          var leadingCall = leading && !timeoutId;
+        } else {
+          if (!maxTimeoutId && !leading) {
+            lastCalled = stamp;
+          }
+          var remaining = maxWait - (stamp - lastCalled),
+              isCalled = remaining <= 0 || remaining > maxWait;
+
+          if (isCalled) {
+            if (maxTimeoutId) {
+              maxTimeoutId = clearTimeout(maxTimeoutId);
+            }
+            lastCalled = stamp;
+            result = func.apply(thisArg, args);
+          }
+          else if (!maxTimeoutId) {
+            maxTimeoutId = setTimeout(maxDelayed, remaining);
+          }
+        }
+        if (isCalled && timeoutId) {
+          timeoutId = clearTimeout(timeoutId);
+        }
+        else if (!timeoutId && wait !== maxWait) {
+          timeoutId = setTimeout(delayed, wait);
+        }
+        if (leadingCall) {
+          isCalled = true;
+          result = func.apply(thisArg, args);
+        }
+        if (isCalled && !timeoutId && !maxTimeoutId) {
+          args = thisArg = undefined;
+        }
+        return result;
+      }
+      debounced.cancel = cancel;
+      return debounced;
+    }
+
+    /**
+     * Defers invoking the `func` until the current call stack has cleared. Any
+     * additional arguments are provided to `func` when it's invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to defer.
+     * @param {...*} [args] The arguments to invoke the function with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.defer(function(text) {
+     *   console.log(text);
+     * }, 'deferred');
+     * // logs 'deferred' after one or more milliseconds
+     */
+    var defer = restParam(function(func, args) {
+      return baseDelay(func, 1, args);
+    });
+
+    /**
+     * Invokes `func` after `wait` milliseconds. Any additional arguments are
+     * provided to `func` when it's invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to delay.
+     * @param {number} wait The number of milliseconds to delay invocation.
+     * @param {...*} [args] The arguments to invoke the function with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.delay(function(text) {
+     *   console.log(text);
+     * }, 1000, 'later');
+     * // => logs 'later' after one second
+     */
+    var delay = restParam(function(func, wait, args) {
+      return baseDelay(func, wait, args);
+    });
+
+    /**
+     * Creates a function that returns the result of invoking the provided
+     * functions with the `this` binding of the created function, where each
+     * successive invocation is supplied the return value of the previous.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {...Function} [funcs] Functions to invoke.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var addSquare = _.flow(_.add, square);
+     * addSquare(1, 2);
+     * // => 9
+     */
+    var flow = createFlow();
+
+    /**
+     * This method is like `_.flow` except that it creates a function that
+     * invokes the provided functions from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias backflow, compose
+     * @category Function
+     * @param {...Function} [funcs] Functions to invoke.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var addSquare = _.flowRight(square, _.add);
+     * addSquare(1, 2);
+     * // => 9
+     */
+    var flowRight = createFlow(true);
+
+    /**
+     * Creates a function that memoizes the result of `func`. If `resolver` is
+     * provided it determines the cache key for storing the result based on the
+     * arguments provided to the memoized function. By default, the first argument
+     * provided to the memoized function is coerced to a string and used as the
+     * cache key. The `func` is invoked with the `this` binding of the memoized
+     * function.
+     *
+     * **Note:** The cache is exposed as the `cache` property on the memoized
+     * function. Its creation may be customized by replacing the `_.memoize.Cache`
+     * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)
+     * method interface of `get`, `has`, and `set`.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to have its output memoized.
+     * @param {Function} [resolver] The function to resolve the cache key.
+     * @returns {Function} Returns the new memoizing function.
+     * @example
+     *
+     * var upperCase = _.memoize(function(string) {
+     *   return string.toUpperCase();
+     * });
+     *
+     * upperCase('fred');
+     * // => 'FRED'
+     *
+     * // modifying the result cache
+     * upperCase.cache.set('fred', 'BARNEY');
+     * upperCase('fred');
+     * // => 'BARNEY'
+     *
+     * // replacing `_.memoize.Cache`
+     * var object = { 'user': 'fred' };
+     * var other = { 'user': 'barney' };
+     * var identity = _.memoize(_.identity);
+     *
+     * identity(object);
+     * // => { 'user': 'fred' }
+     * identity(other);
+     * // => { 'user': 'fred' }
+     *
+     * _.memoize.Cache = WeakMap;
+     * var identity = _.memoize(_.identity);
+     *
+     * identity(object);
+     * // => { 'user': 'fred' }
+     * identity(other);
+     * // => { 'user': 'barney' }
+     */
+    function memoize(func, resolver) {
+      if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      var memoized = function() {
+        var args = arguments,
+            key = resolver ? resolver.apply(this, args) : args[0],
+            cache = memoized.cache;
+
+        if (cache.has(key)) {
+          return cache.get(key);
+        }
+        var result = func.apply(this, args);
+        memoized.cache = cache.set(key, result);
+        return result;
+      };
+      memoized.cache = new memoize.Cache;
+      return memoized;
+    }
+
+    /**
+     * Creates a function that runs each argument through a corresponding
+     * transform function.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to wrap.
+     * @param {...(Function|Function[])} [transforms] The functions to transform
+     * arguments, specified as individual functions or arrays of functions.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * function doubled(n) {
+     *   return n * 2;
+     * }
+     *
+     * function square(n) {
+     *   return n * n;
+     * }
+     *
+     * var modded = _.modArgs(function(x, y) {
+     *   return [x, y];
+     * }, square, doubled);
+     *
+     * modded(1, 2);
+     * // => [1, 4]
+     *
+     * modded(5, 10);
+     * // => [25, 20]
+     */
+    var modArgs = restParam(function(func, transforms) {
+      transforms = baseFlatten(transforms);
+      if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      var length = transforms.length;
+      return restParam(function(args) {
+        var index = nativeMin(args.length, length);
+        while (index--) {
+          args[index] = transforms[index](args[index]);
+        }
+        return func.apply(this, args);
+      });
+    });
+
+    /**
+     * Creates a function that negates the result of the predicate `func`. The
+     * `func` predicate is invoked with the `this` binding and arguments of the
+     * created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} predicate The predicate to negate.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * function isEven(n) {
+     *   return n % 2 == 0;
+     * }
+     *
+     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+     * // => [1, 3, 5]
+     */
+    function negate(predicate) {
+      if (typeof predicate != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      return function() {
+        return !predicate.apply(this, arguments);
+      };
+    }
+
+    /**
+     * Creates a function that is restricted to invoking `func` once. Repeat calls
+     * to the function return the value of the first call. The `func` is invoked
+     * with the `this` binding and arguments of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var initialize = _.once(createApplication);
+     * initialize();
+     * initialize();
+     * // `initialize` invokes `createApplication` once
+     */
+    function once(func) {
+      return before(2, func);
+    }
+
+    /**
+     * Creates a function that invokes `func` with `partial` arguments prepended
+     * to those provided to the new function. This method is like `_.bind` except
+     * it does **not** alter the `this` binding.
+     *
+     * The `_.partial.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for partially applied arguments.
+     *
+     * **Note:** This method does not set the "length" property of partially
+     * applied functions.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * var greet = function(greeting, name) {
+     *   return greeting + ' ' + name;
+     * };
+     *
+     * var sayHelloTo = _.partial(greet, 'hello');
+     * sayHelloTo('fred');
+     * // => 'hello fred'
+     *
+     * // using placeholders
+     * var greetFred = _.partial(greet, _, 'fred');
+     * greetFred('hi');
+     * // => 'hi fred'
+     */
+    var partial = createPartial(PARTIAL_FLAG);
+
+    /**
+     * This method is like `_.partial` except that partially applied arguments
+     * are appended to those provided to the new function.
+     *
+     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
+     * builds, may be used as a placeholder for partially applied arguments.
+     *
+     * **Note:** This method does not set the "length" property of partially
+     * applied functions.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [partials] The arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * var greet = function(greeting, name) {
+     *   return greeting + ' ' + name;
+     * };
+     *
+     * var greetFred = _.partialRight(greet, 'fred');
+     * greetFred('hi');
+     * // => 'hi fred'
+     *
+     * // using placeholders
+     * var sayHelloTo = _.partialRight(greet, 'hello', _);
+     * sayHelloTo('fred');
+     * // => 'hello fred'
+     */
+    var partialRight = createPartial(PARTIAL_RIGHT_FLAG);
+
+    /**
+     * Creates a function that invokes `func` with arguments arranged according
+     * to the specified indexes where the argument value at the first index is
+     * provided as the first argument, the argument value at the second index is
+     * provided as the second argument, and so on.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to rearrange arguments for.
+     * @param {...(number|number[])} indexes The arranged argument indexes,
+     *  specified as individual indexes or arrays of indexes.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var rearged = _.rearg(function(a, b, c) {
+     *   return [a, b, c];
+     * }, 2, 0, 1);
+     *
+     * rearged('b', 'c', 'a')
+     * // => ['a', 'b', 'c']
+     *
+     * var map = _.rearg(_.map, [1, 0]);
+     * map(function(n) {
+     *   return n * 3;
+     * }, [1, 2, 3]);
+     * // => [3, 6, 9]
+     */
+    var rearg = restParam(function(func, indexes) {
+      return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes));
+    });
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of the
+     * created function and arguments from `start` and beyond provided as an array.
+     *
+     * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters).
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to apply a rest parameter to.
+     * @param {number} [start=func.length-1] The start position of the rest parameter.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var say = _.restParam(function(what, names) {
+     *   return what + ' ' + _.initial(names).join(', ') +
+     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
+     * });
+     *
+     * say('hello', 'fred', 'barney', 'pebbles');
+     * // => 'hello fred, barney, & pebbles'
+     */
+    function restParam(func, start) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
+      return function() {
+        var args = arguments,
+            index = -1,
+            length = nativeMax(args.length - start, 0),
+            rest = Array(length);
+
+        while (++index < length) {
+          rest[index] = args[start + index];
+        }
+        switch (start) {
+          case 0: return func.call(this, rest);
+          case 1: return func.call(this, args[0], rest);
+          case 2: return func.call(this, args[0], args[1], rest);
+        }
+        var otherArgs = Array(start + 1);
+        index = -1;
+        while (++index < start) {
+          otherArgs[index] = args[index];
+        }
+        otherArgs[start] = rest;
+        return func.apply(this, otherArgs);
+      };
+    }
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of the created
+     * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3).
+     *
+     * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/Web/JavaScript/Reference/Operators/Spread_operator).
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to spread arguments over.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var say = _.spread(function(who, what) {
+     *   return who + ' says ' + what;
+     * });
+     *
+     * say(['fred', 'hello']);
+     * // => 'fred says hello'
+     *
+     * // with a Promise
+     * var numbers = Promise.all([
+     *   Promise.resolve(40),
+     *   Promise.resolve(36)
+     * ]);
+     *
+     * numbers.then(_.spread(function(x, y) {
+     *   return x + y;
+     * }));
+     * // => a Promise of 76
+     */
+    function spread(func) {
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      return function(array) {
+        return func.apply(this, array);
+      };
+    }
+
+    /**
+     * Creates a throttled function that only invokes `func` at most once per
+     * every `wait` milliseconds. The throttled function comes with a `cancel`
+     * method to cancel delayed invocations. Provide an options object to indicate
+     * that `func` should be invoked on the leading and/or trailing edge of the
+     * `wait` timeout. Subsequent calls to the throttled function return the
+     * result of the last `func` call.
+     *
+     * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
+     * on the trailing edge of the timeout only if the the throttled function is
+     * invoked more than once during the `wait` timeout.
+     *
+     * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
+     * for details over the differences between `_.throttle` and `_.debounce`.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {Function} func The function to throttle.
+     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.leading=true] Specify invoking on the leading
+     *  edge of the timeout.
+     * @param {boolean} [options.trailing=true] Specify invoking on the trailing
+     *  edge of the timeout.
+     * @returns {Function} Returns the new throttled function.
+     * @example
+     *
+     * // avoid excessively updating the position while scrolling
+     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+     *
+     * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
+     * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+     *   'trailing': false
+     * }));
+     *
+     * // cancel a trailing throttled call
+     * jQuery(window).on('popstate', throttled.cancel);
+     */
+    function throttle(func, wait, options) {
+      var leading = true,
+          trailing = true;
+
+      if (typeof func != 'function') {
+        throw new TypeError(FUNC_ERROR_TEXT);
+      }
+      if (options === false) {
+        leading = false;
+      } else if (isObject(options)) {
+        leading = 'leading' in options ? !!options.leading : leading;
+        trailing = 'trailing' in options ? !!options.trailing : trailing;
+      }
+      return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing });
+    }
+
+    /**
+     * Creates a function that provides `value` to the wrapper function as its
+     * first argument. Any additional arguments provided to the function are
+     * appended to those provided to the wrapper function. The wrapper is invoked
+     * with the `this` binding of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Function
+     * @param {*} value The value to wrap.
+     * @param {Function} wrapper The wrapper function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var p = _.wrap(_.escape, function(func, text) {
+     *   return '<p>' + func(text) + '</p>';
+     * });
+     *
+     * p('fred, barney, & pebbles');
+     * // => '<p>fred, barney, &amp; pebbles</p>'
+     */
+    function wrap(value, wrapper) {
+      wrapper = wrapper == null ? identity : wrapper;
+      return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned,
+     * otherwise they are assigned by reference. If `customizer` is provided it's
+     * invoked to produce the cloned values. If `customizer` returns `undefined`
+     * cloning is handled by the method instead. The `customizer` is bound to
+     * `thisArg` and invoked with up to three argument; (value [, index|key, object]).
+     *
+     * **Note:** This method is loosely based on the
+     * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
+     * The enumerable properties of `arguments` objects and objects created by
+     * constructors other than `Object` are cloned to plain `Object` objects. An
+     * empty object is returned for uncloneable values such as functions, DOM nodes,
+     * Maps, Sets, and WeakMaps.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep] Specify a deep clone.
+     * @param {Function} [customizer] The function to customize cloning values.
+     * @param {*} [thisArg] The `this` binding of `customizer`.
+     * @returns {*} Returns the cloned value.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney' },
+     *   { 'user': 'fred' }
+     * ];
+     *
+     * var shallow = _.clone(users);
+     * shallow[0] === users[0];
+     * // => true
+     *
+     * var deep = _.clone(users, true);
+     * deep[0] === users[0];
+     * // => false
+     *
+     * // using a customizer callback
+     * var el = _.clone(document.body, function(value) {
+     *   if (_.isElement(value)) {
+     *     return value.cloneNode(false);
+     *   }
+     * });
+     *
+     * el === document.body
+     * // => false
+     * el.nodeName
+     * // => BODY
+     * el.childNodes.length;
+     * // => 0
+     */
+    function clone(value, isDeep, customizer, thisArg) {
+      if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) {
+        isDeep = false;
+      }
+      else if (typeof isDeep == 'function') {
+        thisArg = customizer;
+        customizer = isDeep;
+        isDeep = false;
+      }
+      return typeof customizer == 'function'
+        ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 3))
+        : baseClone(value, isDeep);
+    }
+
+    /**
+     * Creates a deep clone of `value`. If `customizer` is provided it's invoked
+     * to produce the cloned values. If `customizer` returns `undefined` cloning
+     * is handled by the method instead. The `customizer` is bound to `thisArg`
+     * and invoked with up to three argument; (value [, index|key, object]).
+     *
+     * **Note:** This method is loosely based on the
+     * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
+     * The enumerable properties of `arguments` objects and objects created by
+     * constructors other than `Object` are cloned to plain `Object` objects. An
+     * empty object is returned for uncloneable values such as functions, DOM nodes,
+     * Maps, Sets, and WeakMaps.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to deep clone.
+     * @param {Function} [customizer] The function to customize cloning values.
+     * @param {*} [thisArg] The `this` binding of `customizer`.
+     * @returns {*} Returns the deep cloned value.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney' },
+     *   { 'user': 'fred' }
+     * ];
+     *
+     * var deep = _.cloneDeep(users);
+     * deep[0] === users[0];
+     * // => false
+     *
+     * // using a customizer callback
+     * var el = _.cloneDeep(document.body, function(value) {
+     *   if (_.isElement(value)) {
+     *     return value.cloneNode(true);
+     *   }
+     * });
+     *
+     * el === document.body
+     * // => false
+     * el.nodeName
+     * // => BODY
+     * el.childNodes.length;
+     * // => 20
+     */
+    function cloneDeep(value, customizer, thisArg) {
+      return typeof customizer == 'function'
+        ? baseClone(value, true, bindCallback(customizer, thisArg, 3))
+        : baseClone(value, true);
+    }
+
+    /**
+     * Checks if `value` is greater than `other`.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`.
+     * @example
+     *
+     * _.gt(3, 1);
+     * // => true
+     *
+     * _.gt(3, 3);
+     * // => false
+     *
+     * _.gt(1, 3);
+     * // => false
+     */
+    function gt(value, other) {
+      return value > other;
+    }
+
+    /**
+     * Checks if `value` is greater than or equal to `other`.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`.
+     * @example
+     *
+     * _.gte(3, 1);
+     * // => true
+     *
+     * _.gte(3, 3);
+     * // => true
+     *
+     * _.gte(1, 3);
+     * // => false
+     */
+    function gte(value, other) {
+      return value >= other;
+    }
+
+    /**
+     * Checks if `value` is classified as an `arguments` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isArguments(function() { return arguments; }());
+     * // => true
+     *
+     * _.isArguments([1, 2, 3]);
+     * // => false
+     */
+    function isArguments(value) {
+      return isObjectLike(value) && isArrayLike(value) &&
+        hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
+    }
+
+    /**
+     * Checks if `value` is classified as an `Array` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isArray([1, 2, 3]);
+     * // => true
+     *
+     * _.isArray(function() { return arguments; }());
+     * // => false
+     */
+    var isArray = nativeIsArray || function(value) {
+      return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
+    };
+
+    /**
+     * Checks if `value` is classified as a boolean primitive or object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isBoolean(false);
+     * // => true
+     *
+     * _.isBoolean(null);
+     * // => false
+     */
+    function isBoolean(value) {
+      return value === true || value === false || (isObjectLike(value) && objToString.call(value) == boolTag);
+    }
+
+    /**
+     * Checks if `value` is classified as a `Date` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isDate(new Date);
+     * // => true
+     *
+     * _.isDate('Mon April 23 2012');
+     * // => false
+     */
+    function isDate(value) {
+      return isObjectLike(value) && objToString.call(value) == dateTag;
+    }
+
+    /**
+     * Checks if `value` is a DOM element.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
+     * @example
+     *
+     * _.isElement(document.body);
+     * // => true
+     *
+     * _.isElement('<body>');
+     * // => false
+     */
+    function isElement(value) {
+      return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value);
+    }
+
+    /**
+     * Checks if `value` is empty. A value is considered empty unless it's an
+     * `arguments` object, array, string, or jQuery-like collection with a length
+     * greater than `0` or an object with own enumerable properties.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {Array|Object|string} value The value to inspect.
+     * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+     * @example
+     *
+     * _.isEmpty(null);
+     * // => true
+     *
+     * _.isEmpty(true);
+     * // => true
+     *
+     * _.isEmpty(1);
+     * // => true
+     *
+     * _.isEmpty([1, 2, 3]);
+     * // => false
+     *
+     * _.isEmpty({ 'a': 1 });
+     * // => false
+     */
+    function isEmpty(value) {
+      if (value == null) {
+        return true;
+      }
+      if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
+          (isObjectLike(value) && isFunction(value.splice)))) {
+        return !value.length;
+      }
+      return !keys(value).length;
+    }
+
+    /**
+     * Performs a deep comparison between two values to determine if they are
+     * equivalent. If `customizer` is provided it's invoked to compare values.
+     * If `customizer` returns `undefined` comparisons are handled by the method
+     * instead. The `customizer` is bound to `thisArg` and invoked with up to
+     * three arguments: (value, other [, index|key]).
+     *
+     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+     * numbers, `Object` objects, regexes, and strings. Objects are compared by
+     * their own, not inherited, enumerable properties. Functions and DOM nodes
+     * are **not** supported. Provide a customizer function to extend support
+     * for comparing other values.
+     *
+     * @static
+     * @memberOf _
+     * @alias eq
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @param {Function} [customizer] The function to customize value comparisons.
+     * @param {*} [thisArg] The `this` binding of `customizer`.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     * @example
+     *
+     * var object = { 'user': 'fred' };
+     * var other = { 'user': 'fred' };
+     *
+     * object == other;
+     * // => false
+     *
+     * _.isEqual(object, other);
+     * // => true
+     *
+     * // using a customizer callback
+     * var array = ['hello', 'goodbye'];
+     * var other = ['hi', 'goodbye'];
+     *
+     * _.isEqual(array, other, function(value, other) {
+     *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
+     *     return true;
+     *   }
+     * });
+     * // => true
+     */
+    function isEqual(value, other, customizer, thisArg) {
+      customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
+      var result = customizer ? customizer(value, other) : undefined;
+      return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
+    }
+
+    /**
+     * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
+     * `SyntaxError`, `TypeError`, or `URIError` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
+     * @example
+     *
+     * _.isError(new Error);
+     * // => true
+     *
+     * _.isError(Error);
+     * // => false
+     */
+    function isError(value) {
+      return isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag;
+    }
+
+    /**
+     * Checks if `value` is a finite primitive number.
+     *
+     * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite).
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
+     * @example
+     *
+     * _.isFinite(10);
+     * // => true
+     *
+     * _.isFinite('10');
+     * // => false
+     *
+     * _.isFinite(true);
+     * // => false
+     *
+     * _.isFinite(Object(10));
+     * // => false
+     *
+     * _.isFinite(Infinity);
+     * // => false
+     */
+    function isFinite(value) {
+      return typeof value == 'number' && nativeIsFinite(value);
+    }
+
+    /**
+     * Checks if `value` is classified as a `Function` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isFunction(_);
+     * // => true
+     *
+     * _.isFunction(/abc/);
+     * // => false
+     */
+    function isFunction(value) {
+      // The use of `Object#toString` avoids issues with the `typeof` operator
+      // in older versions of Chrome and Safari which return 'function' for regexes
+      // and Safari 8 which returns 'object' for typed array constructors.
+      return isObject(value) && objToString.call(value) == funcTag;
+    }
+
+    /**
+     * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
+     * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+     * @example
+     *
+     * _.isObject({});
+     * // => true
+     *
+     * _.isObject([1, 2, 3]);
+     * // => true
+     *
+     * _.isObject(1);
+     * // => false
+     */
+    function isObject(value) {
+      // Avoid a V8 JIT bug in Chrome 19-20.
+      // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
+      var type = typeof value;
+      return !!value && (type == 'object' || type == 'function');
+    }
+
+    /**
+     * Performs a deep comparison between `object` and `source` to determine if
+     * `object` contains equivalent property values. If `customizer` is provided
+     * it's invoked to compare values. If `customizer` returns `undefined`
+     * comparisons are handled by the method instead. The `customizer` is bound
+     * to `thisArg` and invoked with three arguments: (value, other, index|key).
+     *
+     * **Note:** This method supports comparing properties of arrays, booleans,
+     * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions
+     * and DOM nodes are **not** supported. Provide a customizer function to extend
+     * support for comparing other values.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {Object} object The object to inspect.
+     * @param {Object} source The object of property values to match.
+     * @param {Function} [customizer] The function to customize value comparisons.
+     * @param {*} [thisArg] The `this` binding of `customizer`.
+     * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+     * @example
+     *
+     * var object = { 'user': 'fred', 'age': 40 };
+     *
+     * _.isMatch(object, { 'age': 40 });
+     * // => true
+     *
+     * _.isMatch(object, { 'age': 36 });
+     * // => false
+     *
+     * // using a customizer callback
+     * var object = { 'greeting': 'hello' };
+     * var source = { 'greeting': 'hi' };
+     *
+     * _.isMatch(object, source, function(value, other) {
+     *   return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined;
+     * });
+     * // => true
+     */
+    function isMatch(object, source, customizer, thisArg) {
+      customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
+      return baseIsMatch(object, getMatchData(source), customizer);
+    }
+
+    /**
+     * Checks if `value` is `NaN`.
+     *
+     * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4)
+     * which returns `true` for `undefined` and other non-numeric values.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+     * @example
+     *
+     * _.isNaN(NaN);
+     * // => true
+     *
+     * _.isNaN(new Number(NaN));
+     * // => true
+     *
+     * isNaN(undefined);
+     * // => true
+     *
+     * _.isNaN(undefined);
+     * // => false
+     */
+    function isNaN(value) {
+      // An `NaN` primitive is the only value that is not equal to itself.
+      // Perform the `toStringTag` check first to avoid errors with some host objects in IE.
+      return isNumber(value) && value != +value;
+    }
+
+    /**
+     * Checks if `value` is a native function.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
+     * @example
+     *
+     * _.isNative(Array.prototype.push);
+     * // => true
+     *
+     * _.isNative(_);
+     * // => false
+     */
+    function isNative(value) {
+      if (value == null) {
+        return false;
+      }
+      if (isFunction(value)) {
+        return reIsNative.test(fnToString.call(value));
+      }
+      return isObjectLike(value) && reIsHostCtor.test(value);
+    }
+
+    /**
+     * Checks if `value` is `null`.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
+     * @example
+     *
+     * _.isNull(null);
+     * // => true
+     *
+     * _.isNull(void 0);
+     * // => false
+     */
+    function isNull(value) {
+      return value === null;
+    }
+
+    /**
+     * Checks if `value` is classified as a `Number` primitive or object.
+     *
+     * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified
+     * as numbers, use the `_.isFinite` method.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isNumber(8.4);
+     * // => true
+     *
+     * _.isNumber(NaN);
+     * // => true
+     *
+     * _.isNumber('8.4');
+     * // => false
+     */
+    function isNumber(value) {
+      return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag);
+    }
+
+    /**
+     * Checks if `value` is a plain object, that is, an object created by the
+     * `Object` constructor or one with a `[[Prototype]]` of `null`.
+     *
+     * **Note:** This method assumes objects created by the `Object` constructor
+     * have no inherited enumerable properties.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     * }
+     *
+     * _.isPlainObject(new Foo);
+     * // => false
+     *
+     * _.isPlainObject([1, 2, 3]);
+     * // => false
+     *
+     * _.isPlainObject({ 'x': 0, 'y': 0 });
+     * // => true
+     *
+     * _.isPlainObject(Object.create(null));
+     * // => true
+     */
+    function isPlainObject(value) {
+      var Ctor;
+
+      // Exit early for non `Object` objects.
+      if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) ||
+          (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
+        return false;
+      }
+      // IE < 9 iterates inherited properties before own properties. If the first
+      // iterated property is an object's own property then there are no inherited
+      // enumerable properties.
+      var result;
+      // In most environments an object's own properties are iterated before
+      // its inherited properties. If the last iterated property is an object's
+      // own property then there are no inherited enumerable properties.
+      baseForIn(value, function(subValue, key) {
+        result = key;
+      });
+      return result === undefined || hasOwnProperty.call(value, result);
+    }
+
+    /**
+     * Checks if `value` is classified as a `RegExp` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isRegExp(/abc/);
+     * // => true
+     *
+     * _.isRegExp('/abc/');
+     * // => false
+     */
+    function isRegExp(value) {
+      return isObject(value) && objToString.call(value) == regexpTag;
+    }
+
+    /**
+     * Checks if `value` is classified as a `String` primitive or object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isString('abc');
+     * // => true
+     *
+     * _.isString(1);
+     * // => false
+     */
+    function isString(value) {
+      return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
+    }
+
+    /**
+     * Checks if `value` is classified as a typed array.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+     * @example
+     *
+     * _.isTypedArray(new Uint8Array);
+     * // => true
+     *
+     * _.isTypedArray([]);
+     * // => false
+     */
+    function isTypedArray(value) {
+      return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
+    }
+
+    /**
+     * Checks if `value` is `undefined`.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
+     * @example
+     *
+     * _.isUndefined(void 0);
+     * // => true
+     *
+     * _.isUndefined(null);
+     * // => false
+     */
+    function isUndefined(value) {
+      return value === undefined;
+    }
+
+    /**
+     * Checks if `value` is less than `other`.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`.
+     * @example
+     *
+     * _.lt(1, 3);
+     * // => true
+     *
+     * _.lt(3, 3);
+     * // => false
+     *
+     * _.lt(3, 1);
+     * // => false
+     */
+    function lt(value, other) {
+      return value < other;
+    }
+
+    /**
+     * Checks if `value` is less than or equal to `other`.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to compare.
+     * @param {*} other The other value to compare.
+     * @returns {boolean} Returns `true` if `value` is less than or equal to `other`, else `false`.
+     * @example
+     *
+     * _.lte(1, 3);
+     * // => true
+     *
+     * _.lte(3, 3);
+     * // => true
+     *
+     * _.lte(3, 1);
+     * // => false
+     */
+    function lte(value, other) {
+      return value <= other;
+    }
+
+    /**
+     * Converts `value` to an array.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {Array} Returns the converted array.
+     * @example
+     *
+     * (function() {
+     *   return _.toArray(arguments).slice(1);
+     * }(1, 2, 3));
+     * // => [2, 3]
+     */
+    function toArray(value) {
+      var length = value ? getLength(value) : 0;
+      if (!isLength(length)) {
+        return values(value);
+      }
+      if (!length) {
+        return [];
+      }
+      return arrayCopy(value);
+    }
+
+    /**
+     * Converts `value` to a plain object flattening inherited enumerable
+     * properties of `value` to own properties of the plain object.
+     *
+     * @static
+     * @memberOf _
+     * @category Lang
+     * @param {*} value The value to convert.
+     * @returns {Object} Returns the converted plain object.
+     * @example
+     *
+     * function Foo() {
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.assign({ 'a': 1 }, new Foo);
+     * // => { 'a': 1, 'b': 2 }
+     *
+     * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
+     * // => { 'a': 1, 'b': 2, 'c': 3 }
+     */
+    function toPlainObject(value) {
+      return baseCopy(value, keysIn(value));
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Recursively merges own enumerable properties of the source object(s), that
+     * don't resolve to `undefined` into the destination object. Subsequent sources
+     * overwrite property assignments of previous sources. If `customizer` is
+     * provided it's invoked to produce the merged values of the destination and
+     * source properties. If `customizer` returns `undefined` merging is handled
+     * by the method instead. The `customizer` is bound to `thisArg` and invoked
+     * with five arguments: (objectValue, sourceValue, key, object, source).
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @param {*} [thisArg] The `this` binding of `customizer`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var users = {
+     *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
+     * };
+     *
+     * var ages = {
+     *   'data': [{ 'age': 36 }, { 'age': 40 }]
+     * };
+     *
+     * _.merge(users, ages);
+     * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
+     *
+     * // using a customizer callback
+     * var object = {
+     *   'fruits': ['apple'],
+     *   'vegetables': ['beet']
+     * };
+     *
+     * var other = {
+     *   'fruits': ['banana'],
+     *   'vegetables': ['carrot']
+     * };
+     *
+     * _.merge(object, other, function(a, b) {
+     *   if (_.isArray(a)) {
+     *     return a.concat(b);
+     *   }
+     * });
+     * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
+     */
+    var merge = createAssigner(baseMerge);
+
+    /**
+     * Assigns own enumerable properties of source object(s) to the destination
+     * object. Subsequent sources overwrite property assignments of previous sources.
+     * If `customizer` is provided it's invoked to produce the assigned values.
+     * The `customizer` is bound to `thisArg` and invoked with five arguments:
+     * (objectValue, sourceValue, key, object, source).
+     *
+     * **Note:** This method mutates `object` and is based on
+     * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign).
+     *
+     * @static
+     * @memberOf _
+     * @alias extend
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @param {Function} [customizer] The function to customize assigned values.
+     * @param {*} [thisArg] The `this` binding of `customizer`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' });
+     * // => { 'user': 'fred', 'age': 40 }
+     *
+     * // using a customizer callback
+     * var defaults = _.partialRight(_.assign, function(value, other) {
+     *   return _.isUndefined(value) ? other : value;
+     * });
+     *
+     * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
+     * // => { 'user': 'barney', 'age': 36 }
+     */
+    var assign = createAssigner(function(object, source, customizer) {
+      return customizer
+        ? assignWith(object, source, customizer)
+        : baseAssign(object, source);
+    });
+
+    /**
+     * Creates an object that inherits from the given `prototype` object. If a
+     * `properties` object is provided its own enumerable properties are assigned
+     * to the created object.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} prototype The object to inherit from.
+     * @param {Object} [properties] The properties to assign to the object.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * function Circle() {
+     *   Shape.call(this);
+     * }
+     *
+     * Circle.prototype = _.create(Shape.prototype, {
+     *   'constructor': Circle
+     * });
+     *
+     * var circle = new Circle;
+     * circle instanceof Circle;
+     * // => true
+     *
+     * circle instanceof Shape;
+     * // => true
+     */
+    function create(prototype, properties, guard) {
+      var result = baseCreate(prototype);
+      if (guard && isIterateeCall(prototype, properties, guard)) {
+        properties = undefined;
+      }
+      return properties ? baseAssign(result, properties) : result;
+    }
+
+    /**
+     * Assigns own enumerable properties of source object(s) to the destination
+     * object for all destination properties that resolve to `undefined`. Once a
+     * property is set, additional values of the same property are ignored.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });
+     * // => { 'user': 'barney', 'age': 36 }
+     */
+    var defaults = createDefaults(assign, assignDefaults);
+
+    /**
+     * This method is like `_.defaults` except that it recursively assigns
+     * default properties.
+     *
+     * **Note:** This method mutates `object`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The destination object.
+     * @param {...Object} [sources] The source objects.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });
+     * // => { 'user': { 'name': 'barney', 'age': 36 } }
+     *
+     */
+    var defaultsDeep = createDefaults(merge, mergeDefaults);
+
+    /**
+     * This method is like `_.find` except that it returns the key of the first
+     * element `predicate` returns truthy for instead of the element itself.
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to search.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
+     * @example
+     *
+     * var users = {
+     *   'barney':  { 'age': 36, 'active': true },
+     *   'fred':    { 'age': 40, 'active': false },
+     *   'pebbles': { 'age': 1,  'active': true }
+     * };
+     *
+     * _.findKey(users, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => 'barney' (iteration order is not guaranteed)
+     *
+     * // using the `_.matches` callback shorthand
+     * _.findKey(users, { 'age': 1, 'active': true });
+     * // => 'pebbles'
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.findKey(users, 'active', false);
+     * // => 'fred'
+     *
+     * // using the `_.property` callback shorthand
+     * _.findKey(users, 'active');
+     * // => 'barney'
+     */
+    var findKey = createFindKey(baseForOwn);
+
+    /**
+     * This method is like `_.findKey` except that it iterates over elements of
+     * a collection in the opposite order.
+     *
+     * If a property name is provided for `predicate` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `predicate` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to search.
+     * @param {Function|Object|string} [predicate=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
+     * @example
+     *
+     * var users = {
+     *   'barney':  { 'age': 36, 'active': true },
+     *   'fred':    { 'age': 40, 'active': false },
+     *   'pebbles': { 'age': 1,  'active': true }
+     * };
+     *
+     * _.findLastKey(users, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => returns `pebbles` assuming `_.findKey` returns `barney`
+     *
+     * // using the `_.matches` callback shorthand
+     * _.findLastKey(users, { 'age': 36, 'active': true });
+     * // => 'barney'
+     *
+     * // using the `_.matchesProperty` callback shorthand
+     * _.findLastKey(users, 'active', false);
+     * // => 'fred'
+     *
+     * // using the `_.property` callback shorthand
+     * _.findLastKey(users, 'active');
+     * // => 'pebbles'
+     */
+    var findLastKey = createFindKey(baseForOwnRight);
+
+    /**
+     * Iterates over own and inherited enumerable properties of an object invoking
+     * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked
+     * with three arguments: (value, key, object). Iteratee functions may exit
+     * iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forIn(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed)
+     */
+    var forIn = createForIn(baseFor);
+
+    /**
+     * This method is like `_.forIn` except that it iterates over properties of
+     * `object` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forInRight(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c'
+     */
+    var forInRight = createForIn(baseForRight);
+
+    /**
+     * Iterates over own enumerable properties of an object invoking `iteratee`
+     * for each property. The `iteratee` is bound to `thisArg` and invoked with
+     * three arguments: (value, key, object). Iteratee functions may exit iteration
+     * early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forOwn(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'a' and 'b' (iteration order is not guaranteed)
+     */
+    var forOwn = createForOwn(baseForOwn);
+
+    /**
+     * This method is like `_.forOwn` except that it iterates over properties of
+     * `object` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.forOwnRight(new Foo, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b'
+     */
+    var forOwnRight = createForOwn(baseForOwnRight);
+
+    /**
+     * Creates an array of function property names from all enumerable properties,
+     * own and inherited, of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @alias methods
+     * @category Object
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns the new array of property names.
+     * @example
+     *
+     * _.functions(_);
+     * // => ['after', 'ary', 'assign', ...]
+     */
+    function functions(object) {
+      return baseFunctions(object, keysIn(object));
+    }
+
+    /**
+     * Gets the property value at `path` of `object`. If the resolved value is
+     * `undefined` the `defaultValue` is used in its place.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to get.
+     * @param {*} [defaultValue] The value returned if the resolved value is `undefined`.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+     *
+     * _.get(object, 'a[0].b.c');
+     * // => 3
+     *
+     * _.get(object, ['a', '0', 'b', 'c']);
+     * // => 3
+     *
+     * _.get(object, 'a.b.c', 'default');
+     * // => 'default'
+     */
+    function get(object, path, defaultValue) {
+      var result = object == null ? undefined : baseGet(object, toPath(path), (path + ''));
+      return result === undefined ? defaultValue : result;
+    }
+
+    /**
+     * Checks if `path` is a direct property.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path to check.
+     * @returns {boolean} Returns `true` if `path` is a direct property, else `false`.
+     * @example
+     *
+     * var object = { 'a': { 'b': { 'c': 3 } } };
+     *
+     * _.has(object, 'a');
+     * // => true
+     *
+     * _.has(object, 'a.b.c');
+     * // => true
+     *
+     * _.has(object, ['a', 'b', 'c']);
+     * // => true
+     */
+    function has(object, path) {
+      if (object == null) {
+        return false;
+      }
+      var result = hasOwnProperty.call(object, path);
+      if (!result && !isKey(path)) {
+        path = toPath(path);
+        object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
+        if (object == null) {
+          return false;
+        }
+        path = last(path);
+        result = hasOwnProperty.call(object, path);
+      }
+      return result || (isLength(object.length) && isIndex(path, object.length) &&
+        (isArray(object) || isArguments(object)));
+    }
+
+    /**
+     * Creates an object composed of the inverted keys and values of `object`.
+     * If `object` contains duplicate values, subsequent values overwrite property
+     * assignments of previous values unless `multiValue` is `true`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to invert.
+     * @param {boolean} [multiValue] Allow multiple values per key.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Object} Returns the new inverted object.
+     * @example
+     *
+     * var object = { 'a': 1, 'b': 2, 'c': 1 };
+     *
+     * _.invert(object);
+     * // => { '1': 'c', '2': 'b' }
+     *
+     * // with `multiValue`
+     * _.invert(object, true);
+     * // => { '1': ['a', 'c'], '2': ['b'] }
+     */
+    function invert(object, multiValue, guard) {
+      if (guard && isIterateeCall(object, multiValue, guard)) {
+        multiValue = undefined;
+      }
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = {};
+
+      while (++index < length) {
+        var key = props[index],
+            value = object[key];
+
+        if (multiValue) {
+          if (hasOwnProperty.call(result, value)) {
+            result[value].push(key);
+          } else {
+            result[value] = [key];
+          }
+        }
+        else {
+          result[value] = key;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array of the own enumerable property names of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects. See the
+     * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
+     * for more details.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.keys(new Foo);
+     * // => ['a', 'b'] (iteration order is not guaranteed)
+     *
+     * _.keys('hi');
+     * // => ['0', '1']
+     */
+    var keys = !nativeKeys ? shimKeys : function(object) {
+      var Ctor = object == null ? undefined : object.constructor;
+      if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
+          (typeof object != 'function' && isArrayLike(object))) {
+        return shimKeys(object);
+      }
+      return isObject(object) ? nativeKeys(object) : [];
+    };
+
+    /**
+     * Creates an array of the own and inherited enumerable property names of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property names.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.keysIn(new Foo);
+     * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
+     */
+    function keysIn(object) {
+      if (object == null) {
+        return [];
+      }
+      if (!isObject(object)) {
+        object = Object(object);
+      }
+      var length = object.length;
+      length = (length && isLength(length) &&
+        (isArray(object) || isArguments(object)) && length) || 0;
+
+      var Ctor = object.constructor,
+          index = -1,
+          isProto = typeof Ctor == 'function' && Ctor.prototype === object,
+          result = Array(length),
+          skipIndexes = length > 0;
+
+      while (++index < length) {
+        result[index] = (index + '');
+      }
+      for (var key in object) {
+        if (!(skipIndexes && isIndex(key, length)) &&
+            !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
+          result.push(key);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The opposite of `_.mapValues`; this method creates an object with the
+     * same values as `object` and keys generated by running each own enumerable
+     * property of `object` through `iteratee`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns the new mapped object.
+     * @example
+     *
+     * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {
+     *   return key + value;
+     * });
+     * // => { 'a1': 1, 'b2': 2 }
+     */
+    var mapKeys = createObjectMapper(true);
+
+    /**
+     * Creates an object with the same keys as `object` and values generated by
+     * running each own enumerable property of `object` through `iteratee`. The
+     * iteratee function is bound to `thisArg` and invoked with three arguments:
+     * (value, key, object).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to iterate over.
+     * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+     *  per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Object} Returns the new mapped object.
+     * @example
+     *
+     * _.mapValues({ 'a': 1, 'b': 2 }, function(n) {
+     *   return n * 3;
+     * });
+     * // => { 'a': 3, 'b': 6 }
+     *
+     * var users = {
+     *   'fred':    { 'user': 'fred',    'age': 40 },
+     *   'pebbles': { 'user': 'pebbles', 'age': 1 }
+     * };
+     *
+     * // using the `_.property` callback shorthand
+     * _.mapValues(users, 'age');
+     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
+     */
+    var mapValues = createObjectMapper();
+
+    /**
+     * The opposite of `_.pick`; this method creates an object composed of the
+     * own and inherited enumerable properties of `object` that are not omitted.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The source object.
+     * @param {Function|...(string|string[])} [predicate] The function invoked per
+     *  iteration or property names to omit, specified as individual property
+     *  names or arrays of property names.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * var object = { 'user': 'fred', 'age': 40 };
+     *
+     * _.omit(object, 'age');
+     * // => { 'user': 'fred' }
+     *
+     * _.omit(object, _.isNumber);
+     * // => { 'user': 'fred' }
+     */
+    var omit = restParam(function(object, props) {
+      if (object == null) {
+        return {};
+      }
+      if (typeof props[0] != 'function') {
+        var props = arrayMap(baseFlatten(props), String);
+        return pickByArray(object, baseDifference(keysIn(object), props));
+      }
+      var predicate = bindCallback(props[0], props[1], 3);
+      return pickByCallback(object, function(value, key, object) {
+        return !predicate(value, key, object);
+      });
+    });
+
+    /**
+     * Creates a two dimensional array of the key-value pairs for `object`,
+     * e.g. `[[key1, value1], [key2, value2]]`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the new array of key-value pairs.
+     * @example
+     *
+     * _.pairs({ 'barney': 36, 'fred': 40 });
+     * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed)
+     */
+    function pairs(object) {
+      object = toObject(object);
+
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = Array(length);
+
+      while (++index < length) {
+        var key = props[index];
+        result[index] = [key, object[key]];
+      }
+      return result;
+    }
+
+    /**
+     * Creates an object composed of the picked `object` properties. Property
+     * names may be specified as individual arguments or as arrays of property
+     * names. If `predicate` is provided it's invoked for each property of `object`
+     * picking the properties `predicate` returns truthy for. The predicate is
+     * bound to `thisArg` and invoked with three arguments: (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The source object.
+     * @param {Function|...(string|string[])} [predicate] The function invoked per
+     *  iteration or property names to pick, specified as individual property
+     *  names or arrays of property names.
+     * @param {*} [thisArg] The `this` binding of `predicate`.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * var object = { 'user': 'fred', 'age': 40 };
+     *
+     * _.pick(object, 'user');
+     * // => { 'user': 'fred' }
+     *
+     * _.pick(object, _.isString);
+     * // => { 'user': 'fred' }
+     */
+    var pick = restParam(function(object, props) {
+      if (object == null) {
+        return {};
+      }
+      return typeof props[0] == 'function'
+        ? pickByCallback(object, bindCallback(props[0], props[1], 3))
+        : pickByArray(object, baseFlatten(props));
+    });
+
+    /**
+     * This method is like `_.get` except that if the resolved value is a function
+     * it's invoked with the `this` binding of its parent object and its result
+     * is returned.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @param {Array|string} path The path of the property to resolve.
+     * @param {*} [defaultValue] The value returned if the resolved value is `undefined`.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };
+     *
+     * _.result(object, 'a[0].b.c1');
+     * // => 3
+     *
+     * _.result(object, 'a[0].b.c2');
+     * // => 4
+     *
+     * _.result(object, 'a.b.c', 'default');
+     * // => 'default'
+     *
+     * _.result(object, 'a.b.c', _.constant('default'));
+     * // => 'default'
+     */
+    function result(object, path, defaultValue) {
+      var result = object == null ? undefined : object[path];
+      if (result === undefined) {
+        if (object != null && !isKey(path, object)) {
+          path = toPath(path);
+          object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));
+          result = object == null ? undefined : object[last(path)];
+        }
+        result = result === undefined ? defaultValue : result;
+      }
+      return isFunction(result) ? result.call(object) : result;
+    }
+
+    /**
+     * Sets the property value of `path` on `object`. If a portion of `path`
+     * does not exist it's created.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to augment.
+     * @param {Array|string} path The path of the property to set.
+     * @param {*} value The value to set.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+     *
+     * _.set(object, 'a[0].b.c', 4);
+     * console.log(object.a[0].b.c);
+     * // => 4
+     *
+     * _.set(object, 'x[0].y.z', 5);
+     * console.log(object.x[0].y.z);
+     * // => 5
+     */
+    function set(object, path, value) {
+      if (object == null) {
+        return object;
+      }
+      var pathKey = (path + '');
+      path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path);
+
+      var index = -1,
+          length = path.length,
+          lastIndex = length - 1,
+          nested = object;
+
+      while (nested != null && ++index < length) {
+        var key = path[index];
+        if (isObject(nested)) {
+          if (index == lastIndex) {
+            nested[key] = value;
+          } else if (nested[key] == null) {
+            nested[key] = isIndex(path[index + 1]) ? [] : {};
+          }
+        }
+        nested = nested[key];
+      }
+      return object;
+    }
+
+    /**
+     * An alternative to `_.reduce`; this method transforms `object` to a new
+     * `accumulator` object which is the result of running each of its own enumerable
+     * properties through `iteratee`, with each invocation potentially mutating
+     * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked
+     * with four arguments: (accumulator, value, key, object). Iteratee functions
+     * may exit iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Array|Object} object The object to iterate over.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [accumulator] The custom accumulator value.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * _.transform([2, 3, 4], function(result, n) {
+     *   result.push(n *= n);
+     *   return n % 2 == 0;
+     * });
+     * // => [4, 9]
+     *
+     * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) {
+     *   result[key] = n * 3;
+     * });
+     * // => { 'a': 3, 'b': 6 }
+     */
+    function transform(object, iteratee, accumulator, thisArg) {
+      var isArr = isArray(object) || isTypedArray(object);
+      iteratee = getCallback(iteratee, thisArg, 4);
+
+      if (accumulator == null) {
+        if (isArr || isObject(object)) {
+          var Ctor = object.constructor;
+          if (isArr) {
+            accumulator = isArray(object) ? new Ctor : [];
+          } else {
+            accumulator = baseCreate(isFunction(Ctor) ? Ctor.prototype : undefined);
+          }
+        } else {
+          accumulator = {};
+        }
+      }
+      (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) {
+        return iteratee(accumulator, value, index, object);
+      });
+      return accumulator;
+    }
+
+    /**
+     * Creates an array of the own enumerable property values of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property values.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.values(new Foo);
+     * // => [1, 2] (iteration order is not guaranteed)
+     *
+     * _.values('hi');
+     * // => ['h', 'i']
+     */
+    function values(object) {
+      return baseValues(object, keys(object));
+    }
+
+    /**
+     * Creates an array of the own and inherited enumerable property values
+     * of `object`.
+     *
+     * **Note:** Non-object values are coerced to objects.
+     *
+     * @static
+     * @memberOf _
+     * @category Object
+     * @param {Object} object The object to query.
+     * @returns {Array} Returns the array of property values.
+     * @example
+     *
+     * function Foo() {
+     *   this.a = 1;
+     *   this.b = 2;
+     * }
+     *
+     * Foo.prototype.c = 3;
+     *
+     * _.valuesIn(new Foo);
+     * // => [1, 2, 3] (iteration order is not guaranteed)
+     */
+    function valuesIn(object) {
+      return baseValues(object, keysIn(object));
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Checks if `n` is between `start` and up to but not including, `end`. If
+     * `end` is not specified it's set to `start` with `start` then set to `0`.
+     *
+     * @static
+     * @memberOf _
+     * @category Number
+     * @param {number} n The number to check.
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @returns {boolean} Returns `true` if `n` is in the range, else `false`.
+     * @example
+     *
+     * _.inRange(3, 2, 4);
+     * // => true
+     *
+     * _.inRange(4, 8);
+     * // => true
+     *
+     * _.inRange(4, 2);
+     * // => false
+     *
+     * _.inRange(2, 2);
+     * // => false
+     *
+     * _.inRange(1.2, 2);
+     * // => true
+     *
+     * _.inRange(5.2, 4);
+     * // => false
+     */
+    function inRange(value, start, end) {
+      start = +start || 0;
+      if (end === undefined) {
+        end = start;
+        start = 0;
+      } else {
+        end = +end || 0;
+      }
+      return value >= nativeMin(start, end) && value < nativeMax(start, end);
+    }
+
+    /**
+     * Produces a random number between `min` and `max` (inclusive). If only one
+     * argument is provided a number between `0` and the given number is returned.
+     * If `floating` is `true`, or either `min` or `max` are floats, a floating-point
+     * number is returned instead of an integer.
+     *
+     * @static
+     * @memberOf _
+     * @category Number
+     * @param {number} [min=0] The minimum possible value.
+     * @param {number} [max=1] The maximum possible value.
+     * @param {boolean} [floating] Specify returning a floating-point number.
+     * @returns {number} Returns the random number.
+     * @example
+     *
+     * _.random(0, 5);
+     * // => an integer between 0 and 5
+     *
+     * _.random(5);
+     * // => also an integer between 0 and 5
+     *
+     * _.random(5, true);
+     * // => a floating-point number between 0 and 5
+     *
+     * _.random(1.2, 5.2);
+     * // => a floating-point number between 1.2 and 5.2
+     */
+    function random(min, max, floating) {
+      if (floating && isIterateeCall(min, max, floating)) {
+        max = floating = undefined;
+      }
+      var noMin = min == null,
+          noMax = max == null;
+
+      if (floating == null) {
+        if (noMax && typeof min == 'boolean') {
+          floating = min;
+          min = 1;
+        }
+        else if (typeof max == 'boolean') {
+          floating = max;
+          noMax = true;
+        }
+      }
+      if (noMin && noMax) {
+        max = 1;
+        noMax = false;
+      }
+      min = +min || 0;
+      if (noMax) {
+        max = min;
+        min = 0;
+      } else {
+        max = +max || 0;
+      }
+      if (floating || min % 1 || max % 1) {
+        var rand = nativeRandom();
+        return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max);
+      }
+      return baseRandom(min, max);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the camel cased string.
+     * @example
+     *
+     * _.camelCase('Foo Bar');
+     * // => 'fooBar'
+     *
+     * _.camelCase('--foo-bar');
+     * // => 'fooBar'
+     *
+     * _.camelCase('__foo_bar__');
+     * // => 'fooBar'
+     */
+    var camelCase = createCompounder(function(result, word, index) {
+      word = word.toLowerCase();
+      return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word);
+    });
+
+    /**
+     * Capitalizes the first character of `string`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to capitalize.
+     * @returns {string} Returns the capitalized string.
+     * @example
+     *
+     * _.capitalize('fred');
+     * // => 'Fred'
+     */
+    function capitalize(string) {
+      string = baseToString(string);
+      return string && (string.charAt(0).toUpperCase() + string.slice(1));
+    }
+
+    /**
+     * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
+     * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to deburr.
+     * @returns {string} Returns the deburred string.
+     * @example
+     *
+     * _.deburr('déjà vu');
+     * // => 'deja vu'
+     */
+    function deburr(string) {
+      string = baseToString(string);
+      return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, '');
+    }
+
+    /**
+     * Checks if `string` ends with the given target string.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to search.
+     * @param {string} [target] The string to search for.
+     * @param {number} [position=string.length] The position to search from.
+     * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`.
+     * @example
+     *
+     * _.endsWith('abc', 'c');
+     * // => true
+     *
+     * _.endsWith('abc', 'b');
+     * // => false
+     *
+     * _.endsWith('abc', 'b', 2);
+     * // => true
+     */
+    function endsWith(string, target, position) {
+      string = baseToString(string);
+      target = (target + '');
+
+      var length = string.length;
+      position = position === undefined
+        ? length
+        : nativeMin(position < 0 ? 0 : (+position || 0), length);
+
+      position -= target.length;
+      return position >= 0 && string.indexOf(target, position) == position;
+    }
+
+    /**
+     * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to
+     * their corresponding HTML entities.
+     *
+     * **Note:** No other characters are escaped. To escape additional characters
+     * use a third-party library like [_he_](https://mths.be/he).
+     *
+     * Though the ">" character is escaped for symmetry, characters like
+     * ">" and "/" don't need escaping in HTML and have no special meaning
+     * unless they're part of a tag or unquoted attribute value.
+     * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
+     * (under "semi-related fun fact") for more details.
+     *
+     * Backticks are escaped because in Internet Explorer < 9, they can break out
+     * of attribute values or HTML comments. See [#59](https://html5sec.org/#59),
+     * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and
+     * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/)
+     * for more details.
+     *
+     * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping)
+     * to reduce XSS vectors.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to escape.
+     * @returns {string} Returns the escaped string.
+     * @example
+     *
+     * _.escape('fred, barney, & pebbles');
+     * // => 'fred, barney, &amp; pebbles'
+     */
+    function escape(string) {
+      // Reset `lastIndex` because in IE < 9 `String#replace` does not.
+      string = baseToString(string);
+      return (string && reHasUnescapedHtml.test(string))
+        ? string.replace(reUnescapedHtml, escapeHtmlChar)
+        : string;
+    }
+
+    /**
+     * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
+     * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to escape.
+     * @returns {string} Returns the escaped string.
+     * @example
+     *
+     * _.escapeRegExp('[lodash](https://lodash.com/)');
+     * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
+     */
+    function escapeRegExp(string) {
+      string = baseToString(string);
+      return (string && reHasRegExpChars.test(string))
+        ? string.replace(reRegExpChars, escapeRegExpChar)
+        : (string || '(?:)');
+    }
+
+    /**
+     * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the kebab cased string.
+     * @example
+     *
+     * _.kebabCase('Foo Bar');
+     * // => 'foo-bar'
+     *
+     * _.kebabCase('fooBar');
+     * // => 'foo-bar'
+     *
+     * _.kebabCase('__foo_bar__');
+     * // => 'foo-bar'
+     */
+    var kebabCase = createCompounder(function(result, word, index) {
+      return result + (index ? '-' : '') + word.toLowerCase();
+    });
+
+    /**
+     * Pads `string` on the left and right sides if it's shorter than `length`.
+     * Padding characters are truncated if they can't be evenly divided by `length`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to pad.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padded string.
+     * @example
+     *
+     * _.pad('abc', 8);
+     * // => '  abc   '
+     *
+     * _.pad('abc', 8, '_-');
+     * // => '_-abc_-_'
+     *
+     * _.pad('abc', 3);
+     * // => 'abc'
+     */
+    function pad(string, length, chars) {
+      string = baseToString(string);
+      length = +length;
+
+      var strLength = string.length;
+      if (strLength >= length || !nativeIsFinite(length)) {
+        return string;
+      }
+      var mid = (length - strLength) / 2,
+          leftLength = nativeFloor(mid),
+          rightLength = nativeCeil(mid);
+
+      chars = createPadding('', rightLength, chars);
+      return chars.slice(0, leftLength) + string + chars;
+    }
+
+    /**
+     * Pads `string` on the left side if it's shorter than `length`. Padding
+     * characters are truncated if they exceed `length`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to pad.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padded string.
+     * @example
+     *
+     * _.padLeft('abc', 6);
+     * // => '   abc'
+     *
+     * _.padLeft('abc', 6, '_-');
+     * // => '_-_abc'
+     *
+     * _.padLeft('abc', 3);
+     * // => 'abc'
+     */
+    var padLeft = createPadDir();
+
+    /**
+     * Pads `string` on the right side if it's shorter than `length`. Padding
+     * characters are truncated if they exceed `length`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to pad.
+     * @param {number} [length=0] The padding length.
+     * @param {string} [chars=' '] The string used as padding.
+     * @returns {string} Returns the padded string.
+     * @example
+     *
+     * _.padRight('abc', 6);
+     * // => 'abc   '
+     *
+     * _.padRight('abc', 6, '_-');
+     * // => 'abc_-_'
+     *
+     * _.padRight('abc', 3);
+     * // => 'abc'
+     */
+    var padRight = createPadDir(true);
+
+    /**
+     * Converts `string` to an integer of the specified radix. If `radix` is
+     * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal,
+     * in which case a `radix` of `16` is used.
+     *
+     * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E)
+     * of `parseInt`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} string The string to convert.
+     * @param {number} [radix] The radix to interpret `value` by.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {number} Returns the converted integer.
+     * @example
+     *
+     * _.parseInt('08');
+     * // => 8
+     *
+     * _.map(['6', '08', '10'], _.parseInt);
+     * // => [6, 8, 10]
+     */
+    function parseInt(string, radix, guard) {
+      // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`.
+      // Chrome fails to trim leading <BOM> whitespace characters.
+      // See https://code.google.com/p/v8/issues/detail?id=3109 for more details.
+      if (guard ? isIterateeCall(string, radix, guard) : radix == null) {
+        radix = 0;
+      } else if (radix) {
+        radix = +radix;
+      }
+      string = trim(string);
+      return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10));
+    }
+
+    /**
+     * Repeats the given string `n` times.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to repeat.
+     * @param {number} [n=0] The number of times to repeat the string.
+     * @returns {string} Returns the repeated string.
+     * @example
+     *
+     * _.repeat('*', 3);
+     * // => '***'
+     *
+     * _.repeat('abc', 2);
+     * // => 'abcabc'
+     *
+     * _.repeat('abc', 0);
+     * // => ''
+     */
+    function repeat(string, n) {
+      var result = '';
+      string = baseToString(string);
+      n = +n;
+      if (n < 1 || !string || !nativeIsFinite(n)) {
+        return result;
+      }
+      // Leverage the exponentiation by squaring algorithm for a faster repeat.
+      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+      do {
+        if (n % 2) {
+          result += string;
+        }
+        n = nativeFloor(n / 2);
+        string += string;
+      } while (n);
+
+      return result;
+    }
+
+    /**
+     * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the snake cased string.
+     * @example
+     *
+     * _.snakeCase('Foo Bar');
+     * // => 'foo_bar'
+     *
+     * _.snakeCase('fooBar');
+     * // => 'foo_bar'
+     *
+     * _.snakeCase('--foo-bar');
+     * // => 'foo_bar'
+     */
+    var snakeCase = createCompounder(function(result, word, index) {
+      return result + (index ? '_' : '') + word.toLowerCase();
+    });
+
+    /**
+     * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to convert.
+     * @returns {string} Returns the start cased string.
+     * @example
+     *
+     * _.startCase('--foo-bar');
+     * // => 'Foo Bar'
+     *
+     * _.startCase('fooBar');
+     * // => 'Foo Bar'
+     *
+     * _.startCase('__foo_bar__');
+     * // => 'Foo Bar'
+     */
+    var startCase = createCompounder(function(result, word, index) {
+      return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1));
+    });
+
+    /**
+     * Checks if `string` starts with the given target string.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to search.
+     * @param {string} [target] The string to search for.
+     * @param {number} [position=0] The position to search from.
+     * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`.
+     * @example
+     *
+     * _.startsWith('abc', 'a');
+     * // => true
+     *
+     * _.startsWith('abc', 'b');
+     * // => false
+     *
+     * _.startsWith('abc', 'b', 1);
+     * // => true
+     */
+    function startsWith(string, target, position) {
+      string = baseToString(string);
+      position = position == null
+        ? 0
+        : nativeMin(position < 0 ? 0 : (+position || 0), string.length);
+
+      return string.lastIndexOf(target, position) == position;
+    }
+
+    /**
+     * Creates a compiled template function that can interpolate data properties
+     * in "interpolate" delimiters, HTML-escape interpolated data properties in
+     * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data
+     * properties may be accessed as free variables in the template. If a setting
+     * object is provided it takes precedence over `_.templateSettings` values.
+     *
+     * **Note:** In the development build `_.template` utilizes
+     * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
+     * for easier debugging.
+     *
+     * For more information on precompiling templates see
+     * [lodash's custom builds documentation](https://lodash.com/custom-builds).
+     *
+     * For more information on Chrome extension sandboxes see
+     * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The template string.
+     * @param {Object} [options] The options object.
+     * @param {RegExp} [options.escape] The HTML "escape" delimiter.
+     * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+     * @param {Object} [options.imports] An object to import into the template as free variables.
+     * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+     * @param {string} [options.sourceURL] The sourceURL of the template's compiled source.
+     * @param {string} [options.variable] The data object variable name.
+     * @param- {Object} [otherOptions] Enables the legacy `options` param signature.
+     * @returns {Function} Returns the compiled template function.
+     * @example
+     *
+     * // using the "interpolate" delimiter to create a compiled template
+     * var compiled = _.template('hello <%= user %>!');
+     * compiled({ 'user': 'fred' });
+     * // => 'hello fred!'
+     *
+     * // using the HTML "escape" delimiter to escape data property values
+     * var compiled = _.template('<b><%- value %></b>');
+     * compiled({ 'value': '<script>' });
+     * // => '<b>&lt;script&gt;</b>'
+     *
+     * // using the "evaluate" delimiter to execute JavaScript and generate HTML
+     * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>');
+     * compiled({ 'users': ['fred', 'barney'] });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // using the internal `print` function in "evaluate" delimiters
+     * var compiled = _.template('<% print("hello " + user); %>!');
+     * compiled({ 'user': 'barney' });
+     * // => 'hello barney!'
+     *
+     * // using the ES delimiter as an alternative to the default "interpolate" delimiter
+     * var compiled = _.template('hello ${ user }!');
+     * compiled({ 'user': 'pebbles' });
+     * // => 'hello pebbles!'
+     *
+     * // using custom template delimiters
+     * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
+     * var compiled = _.template('hello {{ user }}!');
+     * compiled({ 'user': 'mustache' });
+     * // => 'hello mustache!'
+     *
+     * // using backslashes to treat delimiters as plain text
+     * var compiled = _.template('<%= "\\<%- value %\\>" %>');
+     * compiled({ 'value': 'ignored' });
+     * // => '<%- value %>'
+     *
+     * // using the `imports` option to import `jQuery` as `jq`
+     * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>';
+     * var compiled = _.template(text, { 'imports': { 'jq': jQuery } });
+     * compiled({ 'users': ['fred', 'barney'] });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // using the `sourceURL` option to specify a custom sourceURL for the template
+     * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' });
+     * compiled(data);
+     * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+     *
+     * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+     * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' });
+     * compiled.source;
+     * // => function(data) {
+     * //   var __t, __p = '';
+     * //   __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!';
+     * //   return __p;
+     * // }
+     *
+     * // using the `source` property to inline compiled templates for meaningful
+     * // line numbers in error messages and a stack trace
+     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+     *   var JST = {\
+     *     "main": ' + _.template(mainText).source + '\
+     *   };\
+     * ');
+     */
+    function template(string, options, otherOptions) {
+      // Based on John Resig's `tmpl` implementation (http://ejohn.org/blog/javascript-micro-templating/)
+      // and Laura Doktorova's doT.js (https://github.com/olado/doT).
+      var settings = lodash.templateSettings;
+
+      if (otherOptions && isIterateeCall(string, options, otherOptions)) {
+        options = otherOptions = undefined;
+      }
+      string = baseToString(string);
+      options = assignWith(baseAssign({}, otherOptions || options), settings, assignOwnDefaults);
+
+      var imports = assignWith(baseAssign({}, options.imports), settings.imports, assignOwnDefaults),
+          importsKeys = keys(imports),
+          importsValues = baseValues(imports, importsKeys);
+
+      var isEscaping,
+          isEvaluating,
+          index = 0,
+          interpolate = options.interpolate || reNoMatch,
+          source = "__p += '";
+
+      // Compile the regexp to match each delimiter.
+      var reDelimiters = RegExp(
+        (options.escape || reNoMatch).source + '|' +
+        interpolate.source + '|' +
+        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+        (options.evaluate || reNoMatch).source + '|$'
+      , 'g');
+
+      // Use a sourceURL for easier debugging.
+      var sourceURL = '//# sourceURL=' +
+        ('sourceURL' in options
+          ? options.sourceURL
+          : ('lodash.templateSources[' + (++templateCounter) + ']')
+        ) + '\n';
+
+      string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+        interpolateValue || (interpolateValue = esTemplateValue);
+
+        // Escape characters that can't be included in string literals.
+        source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+        // Replace delimiters with snippets.
+        if (escapeValue) {
+          isEscaping = true;
+          source += "' +\n__e(" + escapeValue + ") +\n'";
+        }
+        if (evaluateValue) {
+          isEvaluating = true;
+          source += "';\n" + evaluateValue + ";\n__p += '";
+        }
+        if (interpolateValue) {
+          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+        }
+        index = offset + match.length;
+
+        // The JS engine embedded in Adobe products requires returning the `match`
+        // string in order to produce the correct `offset` value.
+        return match;
+      });
+
+      source += "';\n";
+
+      // If `variable` is not specified wrap a with-statement around the generated
+      // code to add the data object to the top of the scope chain.
+      var variable = options.variable;
+      if (!variable) {
+        source = 'with (obj) {\n' + source + '\n}\n';
+      }
+      // Cleanup code by stripping empty strings.
+      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+        .replace(reEmptyStringMiddle, '$1')
+        .replace(reEmptyStringTrailing, '$1;');
+
+      // Frame code as the function body.
+      source = 'function(' + (variable || 'obj') + ') {\n' +
+        (variable
+          ? ''
+          : 'obj || (obj = {});\n'
+        ) +
+        "var __t, __p = ''" +
+        (isEscaping
+           ? ', __e = _.escape'
+           : ''
+        ) +
+        (isEvaluating
+          ? ', __j = Array.prototype.join;\n' +
+            "function print() { __p += __j.call(arguments, '') }\n"
+          : ';\n'
+        ) +
+        source +
+        'return __p\n}';
+
+      var result = attempt(function() {
+        return Function(importsKeys, sourceURL + 'return ' + source).apply(undefined, importsValues);
+      });
+
+      // Provide the compiled function's source by its `toString` method or
+      // the `source` property as a convenience for inlining compiled templates.
+      result.source = source;
+      if (isError(result)) {
+        throw result;
+      }
+      return result;
+    }
+
+    /**
+     * Removes leading and trailing whitespace or specified characters from `string`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to trim.
+     * @param {string} [chars=whitespace] The characters to trim.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {string} Returns the trimmed string.
+     * @example
+     *
+     * _.trim('  abc  ');
+     * // => 'abc'
+     *
+     * _.trim('-_-abc-_-', '_-');
+     * // => 'abc'
+     *
+     * _.map(['  foo  ', '  bar  '], _.trim);
+     * // => ['foo', 'bar']
+     */
+    function trim(string, chars, guard) {
+      var value = string;
+      string = baseToString(string);
+      if (!string) {
+        return string;
+      }
+      if (guard ? isIterateeCall(value, chars, guard) : chars == null) {
+        return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1);
+      }
+      chars = (chars + '');
+      return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1);
+    }
+
+    /**
+     * Removes leading whitespace or specified characters from `string`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to trim.
+     * @param {string} [chars=whitespace] The characters to trim.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {string} Returns the trimmed string.
+     * @example
+     *
+     * _.trimLeft('  abc  ');
+     * // => 'abc  '
+     *
+     * _.trimLeft('-_-abc-_-', '_-');
+     * // => 'abc-_-'
+     */
+    function trimLeft(string, chars, guard) {
+      var value = string;
+      string = baseToString(string);
+      if (!string) {
+        return string;
+      }
+      if (guard ? isIterateeCall(value, chars, guard) : chars == null) {
+        return string.slice(trimmedLeftIndex(string));
+      }
+      return string.slice(charsLeftIndex(string, (chars + '')));
+    }
+
+    /**
+     * Removes trailing whitespace or specified characters from `string`.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to trim.
+     * @param {string} [chars=whitespace] The characters to trim.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {string} Returns the trimmed string.
+     * @example
+     *
+     * _.trimRight('  abc  ');
+     * // => '  abc'
+     *
+     * _.trimRight('-_-abc-_-', '_-');
+     * // => '-_-abc'
+     */
+    function trimRight(string, chars, guard) {
+      var value = string;
+      string = baseToString(string);
+      if (!string) {
+        return string;
+      }
+      if (guard ? isIterateeCall(value, chars, guard) : chars == null) {
+        return string.slice(0, trimmedRightIndex(string) + 1);
+      }
+      return string.slice(0, charsRightIndex(string, (chars + '')) + 1);
+    }
+
+    /**
+     * Truncates `string` if it's longer than the given maximum string length.
+     * The last characters of the truncated string are replaced with the omission
+     * string which defaults to "...".
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to truncate.
+     * @param {Object|number} [options] The options object or maximum string length.
+     * @param {number} [options.length=30] The maximum string length.
+     * @param {string} [options.omission='...'] The string to indicate text is omitted.
+     * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {string} Returns the truncated string.
+     * @example
+     *
+     * _.trunc('hi-diddly-ho there, neighborino');
+     * // => 'hi-diddly-ho there, neighbo...'
+     *
+     * _.trunc('hi-diddly-ho there, neighborino', 24);
+     * // => 'hi-diddly-ho there, n...'
+     *
+     * _.trunc('hi-diddly-ho there, neighborino', {
+     *   'length': 24,
+     *   'separator': ' '
+     * });
+     * // => 'hi-diddly-ho there,...'
+     *
+     * _.trunc('hi-diddly-ho there, neighborino', {
+     *   'length': 24,
+     *   'separator': /,? +/
+     * });
+     * // => 'hi-diddly-ho there...'
+     *
+     * _.trunc('hi-diddly-ho there, neighborino', {
+     *   'omission': ' [...]'
+     * });
+     * // => 'hi-diddly-ho there, neig [...]'
+     */
+    function trunc(string, options, guard) {
+      if (guard && isIterateeCall(string, options, guard)) {
+        options = undefined;
+      }
+      var length = DEFAULT_TRUNC_LENGTH,
+          omission = DEFAULT_TRUNC_OMISSION;
+
+      if (options != null) {
+        if (isObject(options)) {
+          var separator = 'separator' in options ? options.separator : separator;
+          length = 'length' in options ? (+options.length || 0) : length;
+          omission = 'omission' in options ? baseToString(options.omission) : omission;
+        } else {
+          length = +options || 0;
+        }
+      }
+      string = baseToString(string);
+      if (length >= string.length) {
+        return string;
+      }
+      var end = length - omission.length;
+      if (end < 1) {
+        return omission;
+      }
+      var result = string.slice(0, end);
+      if (separator == null) {
+        return result + omission;
+      }
+      if (isRegExp(separator)) {
+        if (string.slice(end).search(separator)) {
+          var match,
+              newEnd,
+              substring = string.slice(0, end);
+
+          if (!separator.global) {
+            separator = RegExp(separator.source, (reFlags.exec(separator) || '') + 'g');
+          }
+          separator.lastIndex = 0;
+          while ((match = separator.exec(substring))) {
+            newEnd = match.index;
+          }
+          result = result.slice(0, newEnd == null ? end : newEnd);
+        }
+      } else if (string.indexOf(separator, end) != end) {
+        var index = result.lastIndexOf(separator);
+        if (index > -1) {
+          result = result.slice(0, index);
+        }
+      }
+      return result + omission;
+    }
+
+    /**
+     * The inverse of `_.escape`; this method converts the HTML entities
+     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, `&#39;`, and `&#96;` in `string` to their
+     * corresponding characters.
+     *
+     * **Note:** No other HTML entities are unescaped. To unescape additional HTML
+     * entities use a third-party library like [_he_](https://mths.be/he).
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to unescape.
+     * @returns {string} Returns the unescaped string.
+     * @example
+     *
+     * _.unescape('fred, barney, &amp; pebbles');
+     * // => 'fred, barney, & pebbles'
+     */
+    function unescape(string) {
+      string = baseToString(string);
+      return (string && reHasEscapedHtml.test(string))
+        ? string.replace(reEscapedHtml, unescapeHtmlChar)
+        : string;
+    }
+
+    /**
+     * Splits `string` into an array of its words.
+     *
+     * @static
+     * @memberOf _
+     * @category String
+     * @param {string} [string=''] The string to inspect.
+     * @param {RegExp|string} [pattern] The pattern to match words.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Array} Returns the words of `string`.
+     * @example
+     *
+     * _.words('fred, barney, & pebbles');
+     * // => ['fred', 'barney', 'pebbles']
+     *
+     * _.words('fred, barney, & pebbles', /[^, ]+/g);
+     * // => ['fred', 'barney', '&', 'pebbles']
+     */
+    function words(string, pattern, guard) {
+      if (guard && isIterateeCall(string, pattern, guard)) {
+        pattern = undefined;
+      }
+      string = baseToString(string);
+      return string.match(pattern || reWords) || [];
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Attempts to invoke `func`, returning either the result or the caught error
+     * object. Any additional arguments are provided to `func` when it's invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Function} func The function to attempt.
+     * @returns {*} Returns the `func` result or error object.
+     * @example
+     *
+     * // avoid throwing errors for invalid selectors
+     * var elements = _.attempt(function(selector) {
+     *   return document.querySelectorAll(selector);
+     * }, '>_>');
+     *
+     * if (_.isError(elements)) {
+     *   elements = [];
+     * }
+     */
+    var attempt = restParam(function(func, args) {
+      try {
+        return func.apply(undefined, args);
+      } catch(e) {
+        return isError(e) ? e : new Error(e);
+      }
+    });
+
+    /**
+     * Creates a function that invokes `func` with the `this` binding of `thisArg`
+     * and arguments of the created function. If `func` is a property name the
+     * created callback returns the property value for a given element. If `func`
+     * is an object the created callback returns `true` for elements that contain
+     * the equivalent object properties, otherwise it returns `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias iteratee
+     * @category Utility
+     * @param {*} [func=_.identity] The value to convert to a callback.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
+     * @returns {Function} Returns the callback.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 }
+     * ];
+     *
+     * // wrap to create custom callback shorthands
+     * _.callback = _.wrap(_.callback, function(callback, func, thisArg) {
+     *   var match = /^(.+?)__([gl]t)(.+)$/.exec(func);
+     *   if (!match) {
+     *     return callback(func, thisArg);
+     *   }
+     *   return function(object) {
+     *     return match[2] == 'gt'
+     *       ? object[match[1]] > match[3]
+     *       : object[match[1]] < match[3];
+     *   };
+     * });
+     *
+     * _.filter(users, 'age__gt36');
+     * // => [{ 'user': 'fred', 'age': 40 }]
+     */
+    function callback(func, thisArg, guard) {
+      if (guard && isIterateeCall(func, thisArg, guard)) {
+        thisArg = undefined;
+      }
+      return isObjectLike(func)
+        ? matches(func)
+        : baseCallback(func, thisArg);
+    }
+
+    /**
+     * Creates a function that returns `value`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {*} value The value to return from the new function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var object = { 'user': 'fred' };
+     * var getter = _.constant(object);
+     *
+     * getter() === object;
+     * // => true
+     */
+    function constant(value) {
+      return function() {
+        return value;
+      };
+    }
+
+    /**
+     * This method returns the first argument provided to it.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {*} value Any value.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * var object = { 'user': 'fred' };
+     *
+     * _.identity(object) === object;
+     * // => true
+     */
+    function identity(value) {
+      return value;
+    }
+
+    /**
+     * Creates a function that performs a deep comparison between a given object
+     * and `source`, returning `true` if the given object has equivalent property
+     * values, else `false`.
+     *
+     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+     * numbers, `Object` objects, regexes, and strings. Objects are compared by
+     * their own, not inherited, enumerable properties. For comparing a single
+     * own or inherited property value see `_.matchesProperty`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Object} source The object of property values to match.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36, 'active': true },
+     *   { 'user': 'fred',   'age': 40, 'active': false }
+     * ];
+     *
+     * _.filter(users, _.matches({ 'age': 40, 'active': false }));
+     * // => [{ 'user': 'fred', 'age': 40, 'active': false }]
+     */
+    function matches(source) {
+      return baseMatches(baseClone(source, true));
+    }
+
+    /**
+     * Creates a function that compares the property value of `path` on a given
+     * object to `value`.
+     *
+     * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+     * numbers, `Object` objects, regexes, and strings. Objects are compared by
+     * their own, not inherited, enumerable properties.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Array|string} path The path of the property to get.
+     * @param {*} srcValue The value to match.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var users = [
+     *   { 'user': 'barney' },
+     *   { 'user': 'fred' }
+     * ];
+     *
+     * _.find(users, _.matchesProperty('user', 'fred'));
+     * // => { 'user': 'fred' }
+     */
+    function matchesProperty(path, srcValue) {
+      return baseMatchesProperty(path, baseClone(srcValue, true));
+    }
+
+    /**
+     * Creates a function that invokes the method at `path` on a given object.
+     * Any additional arguments are provided to the invoked method.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Array|string} path The path of the method to invoke.
+     * @param {...*} [args] The arguments to invoke the method with.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': { 'b': { 'c': _.constant(2) } } },
+     *   { 'a': { 'b': { 'c': _.constant(1) } } }
+     * ];
+     *
+     * _.map(objects, _.method('a.b.c'));
+     * // => [2, 1]
+     *
+     * _.invoke(_.sortBy(objects, _.method(['a', 'b', 'c'])), 'a.b.c');
+     * // => [1, 2]
+     */
+    var method = restParam(function(path, args) {
+      return function(object) {
+        return invokePath(object, path, args);
+      };
+    });
+
+    /**
+     * The opposite of `_.method`; this method creates a function that invokes
+     * the method at a given path on `object`. Any additional arguments are
+     * provided to the invoked method.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Object} object The object to query.
+     * @param {...*} [args] The arguments to invoke the method with.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var array = _.times(3, _.constant),
+     *     object = { 'a': array, 'b': array, 'c': array };
+     *
+     * _.map(['a[2]', 'c[0]'], _.methodOf(object));
+     * // => [2, 0]
+     *
+     * _.map([['a', '2'], ['c', '0']], _.methodOf(object));
+     * // => [2, 0]
+     */
+    var methodOf = restParam(function(object, args) {
+      return function(path) {
+        return invokePath(object, path, args);
+      };
+    });
+
+    /**
+     * Adds all own enumerable function properties of a source object to the
+     * destination object. If `object` is a function then methods are added to
+     * its prototype as well.
+     *
+     * **Note:** Use `_.runInContext` to create a pristine `lodash` function to
+     * avoid conflicts caused by modifying the original.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Function|Object} [object=lodash] The destination object.
+     * @param {Object} source The object of functions to add.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.chain=true] Specify whether the functions added
+     *  are chainable.
+     * @returns {Function|Object} Returns `object`.
+     * @example
+     *
+     * function vowels(string) {
+     *   return _.filter(string, function(v) {
+     *     return /[aeiou]/i.test(v);
+     *   });
+     * }
+     *
+     * _.mixin({ 'vowels': vowels });
+     * _.vowels('fred');
+     * // => ['e']
+     *
+     * _('fred').vowels().value();
+     * // => ['e']
+     *
+     * _.mixin({ 'vowels': vowels }, { 'chain': false });
+     * _('fred').vowels();
+     * // => ['e']
+     */
+    function mixin(object, source, options) {
+      if (options == null) {
+        var isObj = isObject(source),
+            props = isObj ? keys(source) : undefined,
+            methodNames = (props && props.length) ? baseFunctions(source, props) : undefined;
+
+        if (!(methodNames ? methodNames.length : isObj)) {
+          methodNames = false;
+          options = source;
+          source = object;
+          object = this;
+        }
+      }
+      if (!methodNames) {
+        methodNames = baseFunctions(source, keys(source));
+      }
+      var chain = true,
+          index = -1,
+          isFunc = isFunction(object),
+          length = methodNames.length;
+
+      if (options === false) {
+        chain = false;
+      } else if (isObject(options) && 'chain' in options) {
+        chain = options.chain;
+      }
+      while (++index < length) {
+        var methodName = methodNames[index],
+            func = source[methodName];
+
+        object[methodName] = func;
+        if (isFunc) {
+          object.prototype[methodName] = (function(func) {
+            return function() {
+              var chainAll = this.__chain__;
+              if (chain || chainAll) {
+                var result = object(this.__wrapped__),
+                    actions = result.__actions__ = arrayCopy(this.__actions__);
+
+                actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
+                result.__chain__ = chainAll;
+                return result;
+              }
+              return func.apply(object, arrayPush([this.value()], arguments));
+            };
+          }(func));
+        }
+      }
+      return object;
+    }
+
+    /**
+     * Reverts the `_` variable to its previous value and returns a reference to
+     * the `lodash` function.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @returns {Function} Returns the `lodash` function.
+     * @example
+     *
+     * var lodash = _.noConflict();
+     */
+    function noConflict() {
+      root._ = oldDash;
+      return this;
+    }
+
+    /**
+     * A no-operation function that returns `undefined` regardless of the
+     * arguments it receives.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @example
+     *
+     * var object = { 'user': 'fred' };
+     *
+     * _.noop(object) === undefined;
+     * // => true
+     */
+    function noop() {
+      // No operation performed.
+    }
+
+    /**
+     * Creates a function that returns the property value at `path` on a
+     * given object.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Array|string} path The path of the property to get.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var objects = [
+     *   { 'a': { 'b': { 'c': 2 } } },
+     *   { 'a': { 'b': { 'c': 1 } } }
+     * ];
+     *
+     * _.map(objects, _.property('a.b.c'));
+     * // => [2, 1]
+     *
+     * _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c');
+     * // => [1, 2]
+     */
+    function property(path) {
+      return isKey(path) ? baseProperty(path) : basePropertyDeep(path);
+    }
+
+    /**
+     * The opposite of `_.property`; this method creates a function that returns
+     * the property value at a given path on `object`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {Object} object The object to query.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var array = [0, 1, 2],
+     *     object = { 'a': array, 'b': array, 'c': array };
+     *
+     * _.map(['a[2]', 'c[0]'], _.propertyOf(object));
+     * // => [2, 0]
+     *
+     * _.map([['a', '2'], ['c', '0']], _.propertyOf(object));
+     * // => [2, 0]
+     */
+    function propertyOf(object) {
+      return function(path) {
+        return baseGet(object, toPath(path), (path + ''));
+      };
+    }
+
+    /**
+     * Creates an array of numbers (positive and/or negative) progressing from
+     * `start` up to, but not including, `end`. If `end` is not specified it's
+     * set to `start` with `start` then set to `0`. If `end` is less than `start`
+     * a zero-length range is created unless a negative `step` is specified.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @param {number} [step=1] The value to increment or decrement by.
+     * @returns {Array} Returns the new array of numbers.
+     * @example
+     *
+     * _.range(4);
+     * // => [0, 1, 2, 3]
+     *
+     * _.range(1, 5);
+     * // => [1, 2, 3, 4]
+     *
+     * _.range(0, 20, 5);
+     * // => [0, 5, 10, 15]
+     *
+     * _.range(0, -4, -1);
+     * // => [0, -1, -2, -3]
+     *
+     * _.range(1, 4, 0);
+     * // => [1, 1, 1]
+     *
+     * _.range(0);
+     * // => []
+     */
+    function range(start, end, step) {
+      if (step && isIterateeCall(start, end, step)) {
+        end = step = undefined;
+      }
+      start = +start || 0;
+      step = step == null ? 1 : (+step || 0);
+
+      if (end == null) {
+        end = start;
+        start = 0;
+      } else {
+        end = +end || 0;
+      }
+      // Use `Array(length)` so engines like Chakra and V8 avoid slower modes.
+      // See https://youtu.be/XAqIpGU8ZZk#t=17m25s for more details.
+      var index = -1,
+          length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = start;
+        start += step;
+      }
+      return result;
+    }
+
+    /**
+     * Invokes the iteratee function `n` times, returning an array of the results
+     * of each invocation. The `iteratee` is bound to `thisArg` and invoked with
+     * one argument; (index).
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {number} n The number of times to invoke `iteratee`.
+     * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {Array} Returns the array of results.
+     * @example
+     *
+     * var diceRolls = _.times(3, _.partial(_.random, 1, 6, false));
+     * // => [3, 6, 4]
+     *
+     * _.times(3, function(n) {
+     *   mage.castSpell(n);
+     * });
+     * // => invokes `mage.castSpell(n)` three times with `n` of `0`, `1`, and `2`
+     *
+     * _.times(3, function(n) {
+     *   this.cast(n);
+     * }, mage);
+     * // => also invokes `mage.castSpell(n)` three times
+     */
+    function times(n, iteratee, thisArg) {
+      n = nativeFloor(n);
+
+      // Exit early to avoid a JSC JIT bug in Safari 8
+      // where `Array(0)` is treated as `Array(1)`.
+      if (n < 1 || !nativeIsFinite(n)) {
+        return [];
+      }
+      var index = -1,
+          result = Array(nativeMin(n, MAX_ARRAY_LENGTH));
+
+      iteratee = bindCallback(iteratee, thisArg, 1);
+      while (++index < n) {
+        if (index < MAX_ARRAY_LENGTH) {
+          result[index] = iteratee(index);
+        } else {
+          iteratee(index);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Generates a unique ID. If `prefix` is provided the ID is appended to it.
+     *
+     * @static
+     * @memberOf _
+     * @category Utility
+     * @param {string} [prefix] The value to prefix the ID with.
+     * @returns {string} Returns the unique ID.
+     * @example
+     *
+     * _.uniqueId('contact_');
+     * // => 'contact_104'
+     *
+     * _.uniqueId();
+     * // => '105'
+     */
+    function uniqueId(prefix) {
+      var id = ++idCounter;
+      return baseToString(prefix) + id;
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Adds two numbers.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {number} augend The first number to add.
+     * @param {number} addend The second number to add.
+     * @returns {number} Returns the sum.
+     * @example
+     *
+     * _.add(6, 4);
+     * // => 10
+     */
+    function add(augend, addend) {
+      return (+augend || 0) + (+addend || 0);
+    }
+
+    /**
+     * Calculates `n` rounded up to `precision`.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {number} n The number to round up.
+     * @param {number} [precision=0] The precision to round up to.
+     * @returns {number} Returns the rounded up number.
+     * @example
+     *
+     * _.ceil(4.006);
+     * // => 5
+     *
+     * _.ceil(6.004, 2);
+     * // => 6.01
+     *
+     * _.ceil(6040, -2);
+     * // => 6100
+     */
+    var ceil = createRound('ceil');
+
+    /**
+     * Calculates `n` rounded down to `precision`.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {number} n The number to round down.
+     * @param {number} [precision=0] The precision to round down to.
+     * @returns {number} Returns the rounded down number.
+     * @example
+     *
+     * _.floor(4.006);
+     * // => 4
+     *
+     * _.floor(0.046, 2);
+     * // => 0.04
+     *
+     * _.floor(4060, -2);
+     * // => 4000
+     */
+    var floor = createRound('floor');
+
+    /**
+     * Gets the maximum value of `collection`. If `collection` is empty or falsey
+     * `-Infinity` is returned. If an iteratee function is provided it's invoked
+     * for each value in `collection` to generate the criterion by which the value
+     * is ranked. The `iteratee` is bound to `thisArg` and invoked with three
+     * arguments: (value, index, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {*} Returns the maximum value.
+     * @example
+     *
+     * _.max([4, 2, 8, 6]);
+     * // => 8
+     *
+     * _.max([]);
+     * // => -Infinity
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.max(users, function(chr) {
+     *   return chr.age;
+     * });
+     * // => { 'user': 'fred', 'age': 40 }
+     *
+     * // using the `_.property` callback shorthand
+     * _.max(users, 'age');
+     * // => { 'user': 'fred', 'age': 40 }
+     */
+    var max = createExtremum(gt, NEGATIVE_INFINITY);
+
+    /**
+     * Gets the minimum value of `collection`. If `collection` is empty or falsey
+     * `Infinity` is returned. If an iteratee function is provided it's invoked
+     * for each value in `collection` to generate the criterion by which the value
+     * is ranked. The `iteratee` is bound to `thisArg` and invoked with three
+     * arguments: (value, index, collection).
+     *
+     * If a property name is provided for `iteratee` the created `_.property`
+     * style callback returns the property value of the given element.
+     *
+     * If a value is also provided for `thisArg` the created `_.matchesProperty`
+     * style callback returns `true` for elements that have a matching property
+     * value, else `false`.
+     *
+     * If an object is provided for `iteratee` the created `_.matches` style
+     * callback returns `true` for elements that have the properties of the given
+     * object, else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {*} Returns the minimum value.
+     * @example
+     *
+     * _.min([4, 2, 8, 6]);
+     * // => 2
+     *
+     * _.min([]);
+     * // => Infinity
+     *
+     * var users = [
+     *   { 'user': 'barney', 'age': 36 },
+     *   { 'user': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.min(users, function(chr) {
+     *   return chr.age;
+     * });
+     * // => { 'user': 'barney', 'age': 36 }
+     *
+     * // using the `_.property` callback shorthand
+     * _.min(users, 'age');
+     * // => { 'user': 'barney', 'age': 36 }
+     */
+    var min = createExtremum(lt, POSITIVE_INFINITY);
+
+    /**
+     * Calculates `n` rounded to `precision`.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {number} n The number to round.
+     * @param {number} [precision=0] The precision to round to.
+     * @returns {number} Returns the rounded number.
+     * @example
+     *
+     * _.round(4.006);
+     * // => 4
+     *
+     * _.round(4.006, 2);
+     * // => 4.01
+     *
+     * _.round(4060, -2);
+     * // => 4100
+     */
+    var round = createRound('round');
+
+    /**
+     * Gets the sum of the values in `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @category Math
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [iteratee] The function invoked per iteration.
+     * @param {*} [thisArg] The `this` binding of `iteratee`.
+     * @returns {number} Returns the sum.
+     * @example
+     *
+     * _.sum([4, 6]);
+     * // => 10
+     *
+     * _.sum({ 'a': 4, 'b': 6 });
+     * // => 10
+     *
+     * var objects = [
+     *   { 'n': 4 },
+     *   { 'n': 6 }
+     * ];
+     *
+     * _.sum(objects, function(object) {
+     *   return object.n;
+     * });
+     * // => 10
+     *
+     * // using the `_.property` callback shorthand
+     * _.sum(objects, 'n');
+     * // => 10
+     */
+    function sum(collection, iteratee, thisArg) {
+      if (thisArg && isIterateeCall(collection, iteratee, thisArg)) {
+        iteratee = undefined;
+      }
+      iteratee = getCallback(iteratee, thisArg, 3);
+      return iteratee.length == 1
+        ? arraySum(isArray(collection) ? collection : toIterable(collection), iteratee)
+        : baseSum(collection, iteratee);
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    // Ensure wrappers are instances of `baseLodash`.
+    lodash.prototype = baseLodash.prototype;
+
+    LodashWrapper.prototype = baseCreate(baseLodash.prototype);
+    LodashWrapper.prototype.constructor = LodashWrapper;
+
+    LazyWrapper.prototype = baseCreate(baseLodash.prototype);
+    LazyWrapper.prototype.constructor = LazyWrapper;
+
+    // Add functions to the `Map` cache.
+    MapCache.prototype['delete'] = mapDelete;
+    MapCache.prototype.get = mapGet;
+    MapCache.prototype.has = mapHas;
+    MapCache.prototype.set = mapSet;
+
+    // Add functions to the `Set` cache.
+    SetCache.prototype.push = cachePush;
+
+    // Assign cache to `_.memoize`.
+    memoize.Cache = MapCache;
+
+    // Add functions that return wrapped values when chaining.
+    lodash.after = after;
+    lodash.ary = ary;
+    lodash.assign = assign;
+    lodash.at = at;
+    lodash.before = before;
+    lodash.bind = bind;
+    lodash.bindAll = bindAll;
+    lodash.bindKey = bindKey;
+    lodash.callback = callback;
+    lodash.chain = chain;
+    lodash.chunk = chunk;
+    lodash.compact = compact;
+    lodash.constant = constant;
+    lodash.countBy = countBy;
+    lodash.create = create;
+    lodash.curry = curry;
+    lodash.curryRight = curryRight;
+    lodash.debounce = debounce;
+    lodash.defaults = defaults;
+    lodash.defaultsDeep = defaultsDeep;
+    lodash.defer = defer;
+    lodash.delay = delay;
+    lodash.difference = difference;
+    lodash.drop = drop;
+    lodash.dropRight = dropRight;
+    lodash.dropRightWhile = dropRightWhile;
+    lodash.dropWhile = dropWhile;
+    lodash.fill = fill;
+    lodash.filter = filter;
+    lodash.flatten = flatten;
+    lodash.flattenDeep = flattenDeep;
+    lodash.flow = flow;
+    lodash.flowRight = flowRight;
+    lodash.forEach = forEach;
+    lodash.forEachRight = forEachRight;
+    lodash.forIn = forIn;
+    lodash.forInRight = forInRight;
+    lodash.forOwn = forOwn;
+    lodash.forOwnRight = forOwnRight;
+    lodash.functions = functions;
+    lodash.groupBy = groupBy;
+    lodash.indexBy = indexBy;
+    lodash.initial = initial;
+    lodash.intersection = intersection;
+    lodash.invert = invert;
+    lodash.invoke = invoke;
+    lodash.keys = keys;
+    lodash.keysIn = keysIn;
+    lodash.map = map;
+    lodash.mapKeys = mapKeys;
+    lodash.mapValues = mapValues;
+    lodash.matches = matches;
+    lodash.matchesProperty = matchesProperty;
+    lodash.memoize = memoize;
+    lodash.merge = merge;
+    lodash.method = method;
+    lodash.methodOf = methodOf;
+    lodash.mixin = mixin;
+    lodash.modArgs = modArgs;
+    lodash.negate = negate;
+    lodash.omit = omit;
+    lodash.once = once;
+    lodash.pairs = pairs;
+    lodash.partial = partial;
+    lodash.partialRight = partialRight;
+    lodash.partition = partition;
+    lodash.pick = pick;
+    lodash.pluck = pluck;
+    lodash.property = property;
+    lodash.propertyOf = propertyOf;
+    lodash.pull = pull;
+    lodash.pullAt = pullAt;
+    lodash.range = range;
+    lodash.rearg = rearg;
+    lodash.reject = reject;
+    lodash.remove = remove;
+    lodash.rest = rest;
+    lodash.restParam = restParam;
+    lodash.set = set;
+    lodash.shuffle = shuffle;
+    lodash.slice = slice;
+    lodash.sortBy = sortBy;
+    lodash.sortByAll = sortByAll;
+    lodash.sortByOrder = sortByOrder;
+    lodash.spread = spread;
+    lodash.take = take;
+    lodash.takeRight = takeRight;
+    lodash.takeRightWhile = takeRightWhile;
+    lodash.takeWhile = takeWhile;
+    lodash.tap = tap;
+    lodash.throttle = throttle;
+    lodash.thru = thru;
+    lodash.times = times;
+    lodash.toArray = toArray;
+    lodash.toPlainObject = toPlainObject;
+    lodash.transform = transform;
+    lodash.union = union;
+    lodash.uniq = uniq;
+    lodash.unzip = unzip;
+    lodash.unzipWith = unzipWith;
+    lodash.values = values;
+    lodash.valuesIn = valuesIn;
+    lodash.where = where;
+    lodash.without = without;
+    lodash.wrap = wrap;
+    lodash.xor = xor;
+    lodash.zip = zip;
+    lodash.zipObject = zipObject;
+    lodash.zipWith = zipWith;
+
+    // Add aliases.
+    lodash.backflow = flowRight;
+    lodash.collect = map;
+    lodash.compose = flowRight;
+    lodash.each = forEach;
+    lodash.eachRight = forEachRight;
+    lodash.extend = assign;
+    lodash.iteratee = callback;
+    lodash.methods = functions;
+    lodash.object = zipObject;
+    lodash.select = filter;
+    lodash.tail = rest;
+    lodash.unique = uniq;
+
+    // Add functions to `lodash.prototype`.
+    mixin(lodash, lodash);
+
+    /*------------------------------------------------------------------------*/
+
+    // Add functions that return unwrapped values when chaining.
+    lodash.add = add;
+    lodash.attempt = attempt;
+    lodash.camelCase = camelCase;
+    lodash.capitalize = capitalize;
+    lodash.ceil = ceil;
+    lodash.clone = clone;
+    lodash.cloneDeep = cloneDeep;
+    lodash.deburr = deburr;
+    lodash.endsWith = endsWith;
+    lodash.escape = escape;
+    lodash.escapeRegExp = escapeRegExp;
+    lodash.every = every;
+    lodash.find = find;
+    lodash.findIndex = findIndex;
+    lodash.findKey = findKey;
+    lodash.findLast = findLast;
+    lodash.findLastIndex = findLastIndex;
+    lodash.findLastKey = findLastKey;
+    lodash.findWhere = findWhere;
+    lodash.first = first;
+    lodash.floor = floor;
+    lodash.get = get;
+    lodash.gt = gt;
+    lodash.gte = gte;
+    lodash.has = has;
+    lodash.identity = identity;
+    lodash.includes = includes;
+    lodash.indexOf = indexOf;
+    lodash.inRange = inRange;
+    lodash.isArguments = isArguments;
+    lodash.isArray = isArray;
+    lodash.isBoolean = isBoolean;
+    lodash.isDate = isDate;
+    lodash.isElement = isElement;
+    lodash.isEmpty = isEmpty;
+    lodash.isEqual = isEqual;
+    lodash.isError = isError;
+    lodash.isFinite = isFinite;
+    lodash.isFunction = isFunction;
+    lodash.isMatch = isMatch;
+    lodash.isNaN = isNaN;
+    lodash.isNative = isNative;
+    lodash.isNull = isNull;
+    lodash.isNumber = isNumber;
+    lodash.isObject = isObject;
+    lodash.isPlainObject = isPlainObject;
+    lodash.isRegExp = isRegExp;
+    lodash.isString = isString;
+    lodash.isTypedArray = isTypedArray;
+    lodash.isUndefined = isUndefined;
+    lodash.kebabCase = kebabCase;
+    lodash.last = last;
+    lodash.lastIndexOf = lastIndexOf;
+    lodash.lt = lt;
+    lodash.lte = lte;
+    lodash.max = max;
+    lodash.min = min;
+    lodash.noConflict = noConflict;
+    lodash.noop = noop;
+    lodash.now = now;
+    lodash.pad = pad;
+    lodash.padLeft = padLeft;
+    lodash.padRight = padRight;
+    lodash.parseInt = parseInt;
+    lodash.random = random;
+    lodash.reduce = reduce;
+    lodash.reduceRight = reduceRight;
+    lodash.repeat = repeat;
+    lodash.result = result;
+    lodash.round = round;
+    lodash.runInContext = runInContext;
+    lodash.size = size;
+    lodash.snakeCase = snakeCase;
+    lodash.some = some;
+    lodash.sortedIndex = sortedIndex;
+    lodash.sortedLastIndex = sortedLastIndex;
+    lodash.startCase = startCase;
+    lodash.startsWith = startsWith;
+    lodash.sum = sum;
+    lodash.template = template;
+    lodash.trim = trim;
+    lodash.trimLeft = trimLeft;
+    lodash.trimRight = trimRight;
+    lodash.trunc = trunc;
+    lodash.unescape = unescape;
+    lodash.uniqueId = uniqueId;
+    lodash.words = words;
+
+    // Add aliases.
+    lodash.all = every;
+    lodash.any = some;
+    lodash.contains = includes;
+    lodash.eq = isEqual;
+    lodash.detect = find;
+    lodash.foldl = reduce;
+    lodash.foldr = reduceRight;
+    lodash.head = first;
+    lodash.include = includes;
+    lodash.inject = reduce;
+
+    mixin(lodash, (function() {
+      var source = {};
+      baseForOwn(lodash, function(func, methodName) {
+        if (!lodash.prototype[methodName]) {
+          source[methodName] = func;
+        }
+      });
+      return source;
+    }()), false);
+
+    /*------------------------------------------------------------------------*/
+
+    // Add functions capable of returning wrapped and unwrapped values when chaining.
+    lodash.sample = sample;
+
+    lodash.prototype.sample = function(n) {
+      if (!this.__chain__ && n == null) {
+        return sample(this.value());
+      }
+      return this.thru(function(value) {
+        return sample(value, n);
+      });
+    };
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * The semantic version number.
+     *
+     * @static
+     * @memberOf _
+     * @type string
+     */
+    lodash.VERSION = VERSION;
+
+    // Assign default placeholders.
+    arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) {
+      lodash[methodName].placeholder = lodash;
+    });
+
+    // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
+    arrayEach(['drop', 'take'], function(methodName, index) {
+      LazyWrapper.prototype[methodName] = function(n) {
+        var filtered = this.__filtered__;
+        if (filtered && !index) {
+          return new LazyWrapper(this);
+        }
+        n = n == null ? 1 : nativeMax(nativeFloor(n) || 0, 0);
+
+        var result = this.clone();
+        if (filtered) {
+          result.__takeCount__ = nativeMin(result.__takeCount__, n);
+        } else {
+          result.__views__.push({ 'size': n, 'type': methodName + (result.__dir__ < 0 ? 'Right' : '') });
+        }
+        return result;
+      };
+
+      LazyWrapper.prototype[methodName + 'Right'] = function(n) {
+        return this.reverse()[methodName](n).reverse();
+      };
+    });
+
+    // Add `LazyWrapper` methods that accept an `iteratee` value.
+    arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
+      var type = index + 1,
+          isFilter = type != LAZY_MAP_FLAG;
+
+      LazyWrapper.prototype[methodName] = function(iteratee, thisArg) {
+        var result = this.clone();
+        result.__iteratees__.push({ 'iteratee': getCallback(iteratee, thisArg, 1), 'type': type });
+        result.__filtered__ = result.__filtered__ || isFilter;
+        return result;
+      };
+    });
+
+    // Add `LazyWrapper` methods for `_.first` and `_.last`.
+    arrayEach(['first', 'last'], function(methodName, index) {
+      var takeName = 'take' + (index ? 'Right' : '');
+
+      LazyWrapper.prototype[methodName] = function() {
+        return this[takeName](1).value()[0];
+      };
+    });
+
+    // Add `LazyWrapper` methods for `_.initial` and `_.rest`.
+    arrayEach(['initial', 'rest'], function(methodName, index) {
+      var dropName = 'drop' + (index ? '' : 'Right');
+
+      LazyWrapper.prototype[methodName] = function() {
+        return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
+      };
+    });
+
+    // Add `LazyWrapper` methods for `_.pluck` and `_.where`.
+    arrayEach(['pluck', 'where'], function(methodName, index) {
+      var operationName = index ? 'filter' : 'map',
+          createCallback = index ? baseMatches : property;
+
+      LazyWrapper.prototype[methodName] = function(value) {
+        return this[operationName](createCallback(value));
+      };
+    });
+
+    LazyWrapper.prototype.compact = function() {
+      return this.filter(identity);
+    };
+
+    LazyWrapper.prototype.reject = function(predicate, thisArg) {
+      predicate = getCallback(predicate, thisArg, 1);
+      return this.filter(function(value) {
+        return !predicate(value);
+      });
+    };
+
+    LazyWrapper.prototype.slice = function(start, end) {
+      start = start == null ? 0 : (+start || 0);
+
+      var result = this;
+      if (result.__filtered__ && (start > 0 || end < 0)) {
+        return new LazyWrapper(result);
+      }
+      if (start < 0) {
+        result = result.takeRight(-start);
+      } else if (start) {
+        result = result.drop(start);
+      }
+      if (end !== undefined) {
+        end = (+end || 0);
+        result = end < 0 ? result.dropRight(-end) : result.take(end - start);
+      }
+      return result;
+    };
+
+    LazyWrapper.prototype.takeRightWhile = function(predicate, thisArg) {
+      return this.reverse().takeWhile(predicate, thisArg).reverse();
+    };
+
+    LazyWrapper.prototype.toArray = function() {
+      return this.take(POSITIVE_INFINITY);
+    };
+
+    // Add `LazyWrapper` methods to `lodash.prototype`.
+    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
+      var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName),
+          retUnwrapped = /^(?:first|last)$/.test(methodName),
+          lodashFunc = lodash[retUnwrapped ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName];
+
+      if (!lodashFunc) {
+        return;
+      }
+      lodash.prototype[methodName] = function() {
+        var args = retUnwrapped ? [1] : arguments,
+            chainAll = this.__chain__,
+            value = this.__wrapped__,
+            isHybrid = !!this.__actions__.length,
+            isLazy = value instanceof LazyWrapper,
+            iteratee = args[0],
+            useLazy = isLazy || isArray(value);
+
+        if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
+          // Avoid lazy use if the iteratee has a "length" value other than `1`.
+          isLazy = useLazy = false;
+        }
+        var interceptor = function(value) {
+          return (retUnwrapped && chainAll)
+            ? lodashFunc(value, 1)[0]
+            : lodashFunc.apply(undefined, arrayPush([value], args));
+        };
+
+        var action = { 'func': thru, 'args': [interceptor], 'thisArg': undefined },
+            onlyLazy = isLazy && !isHybrid;
+
+        if (retUnwrapped && !chainAll) {
+          if (onlyLazy) {
+            value = value.clone();
+            value.__actions__.push(action);
+            return func.call(value);
+          }
+          return lodashFunc.call(undefined, this.value())[0];
+        }
+        if (!retUnwrapped && useLazy) {
+          value = onlyLazy ? value : new LazyWrapper(this);
+          var result = func.apply(value, args);
+          result.__actions__.push(action);
+          return new LodashWrapper(result, chainAll);
+        }
+        return this.thru(interceptor);
+      };
+    });
+
+    // Add `Array` and `String` methods to `lodash.prototype`.
+    arrayEach(['join', 'pop', 'push', 'replace', 'shift', 'sort', 'splice', 'split', 'unshift'], function(methodName) {
+      var func = (/^(?:replace|split)$/.test(methodName) ? stringProto : arrayProto)[methodName],
+          chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
+          retUnwrapped = /^(?:join|pop|replace|shift)$/.test(methodName);
+
+      lodash.prototype[methodName] = function() {
+        var args = arguments;
+        if (retUnwrapped && !this.__chain__) {
+          return func.apply(this.value(), args);
+        }
+        return this[chainName](function(value) {
+          return func.apply(value, args);
+        });
+      };
+    });
+
+    // Map minified function names to their real names.
+    baseForOwn(LazyWrapper.prototype, function(func, methodName) {
+      var lodashFunc = lodash[methodName];
+      if (lodashFunc) {
+        var key = (lodashFunc.name + ''),
+            names = realNames[key] || (realNames[key] = []);
+
+        names.push({ 'name': methodName, 'func': lodashFunc });
+      }
+    });
+
+    realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{ 'name': 'wrapper', 'func': undefined }];
+
+    // Add functions to the lazy wrapper.
+    LazyWrapper.prototype.clone = lazyClone;
+    LazyWrapper.prototype.reverse = lazyReverse;
+    LazyWrapper.prototype.value = lazyValue;
+
+    // Add chaining functions to the `lodash` wrapper.
+    lodash.prototype.chain = wrapperChain;
+    lodash.prototype.commit = wrapperCommit;
+    lodash.prototype.concat = wrapperConcat;
+    lodash.prototype.plant = wrapperPlant;
+    lodash.prototype.reverse = wrapperReverse;
+    lodash.prototype.toString = wrapperToString;
+    lodash.prototype.run = lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
+
+    // Add function aliases to the `lodash` wrapper.
+    lodash.prototype.collect = lodash.prototype.map;
+    lodash.prototype.head = lodash.prototype.first;
+    lodash.prototype.select = lodash.prototype.filter;
+    lodash.prototype.tail = lodash.prototype.rest;
+
+    return lodash;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // Export lodash.
+  var _ = runInContext();
+
+  module.exports = _
+  
+}.call(this));
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/.npmignore
new file mode 100644
index 0000000..b83202d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/.npmignore
@@ -0,0 +1,13 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/README.md
new file mode 100644
index 0000000..eb25570
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/README.md
@@ -0,0 +1,15 @@
+# jade-filters
+
+Code for processing filters in jade templates
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-filters/master.svg)](https://travis-ci.org/jadejs/jade-filters)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-filters.svg)](https://gemnasium.com/jadejs/jade-filters)
+[![NPM version](https://img.shields.io/npm/v/jade-filters.svg)](https://www.npmjs.org/package/jade-filters)
+
+## Installation
+
+    npm install jade-filters
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/index.js
new file mode 100644
index 0000000..7a68795
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/index.js
@@ -0,0 +1,4 @@
+'use strict';
+
+exports.runFilter = require('./lib/run-filter');
+exports.handleFilters = require('./lib/handle-filters');
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/lib/handle-filters.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/lib/handle-filters.js
new file mode 100644
index 0000000..fca2b7f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/lib/handle-filters.js
@@ -0,0 +1,44 @@
+'use strict';
+
+var dirname = require('path').dirname;
+var constantinople = require('constantinople');
+var walk = require('jade-walk');
+var error = require('jade-error');
+var runFilter = require('./run-filter');
+
+module.exports = function handleFilters(ast, filters) {
+  walk(ast, function (node) {
+    if (node.type === 'Filter') {
+      if (node.block.nodes[0] && node.block.nodes[0].type === 'Filter') {
+        node.block.nodes[0] = handleFilters(node.block, filters).nodes[0];
+      }
+      var text = node.block.nodes.map(
+        function(node){ return node.val; }
+      ).join('');
+      var attrs = {};
+      node.attrs.forEach(function (attr) {
+        attrs[attr.name] = constantinople.toConstant(attr.val);
+      });
+      attrs.filename = node.filename;
+      node.type = 'Text';
+      try {
+        if (filters && filters[node.name]) {
+          node.val = filters[node.name](text, attrs);
+        } else {
+          var dir = node.filename ? dirname(node.filename) : null;
+          node.val = runFilter(node.name, text, attrs, dir);
+        }
+      } catch (ex) {
+        if (ex.code === 'UNKNOWN_FILTER') {
+          var err = error(ex.code, ex.message, {
+            line: node.line,
+            filename: node.filename
+          });
+          throw err;
+        }
+        throw ex;
+      }
+    }
+  });
+  return ast;
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/lib/run-filter.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/lib/run-filter.js
new file mode 100644
index 0000000..ed7bed0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/lib/run-filter.js
@@ -0,0 +1,42 @@
+'use strict';
+
+var jstransformer = require('jstransformer');
+var uglify = require('uglify-js');
+var CleanCSS = require('clean-css');
+var resolve = require('resolve');
+
+module.exports = filter;
+function filter(name, str, options, currentDirectory) {
+  var tr;
+  try {
+    try {
+      tr = require(resolve.sync('jstransformer-' + name, {basedir: currentDirectory || process.cwd()}));
+    } catch (ex) {
+      tr = require('jstransformer-' + name);
+    }
+    tr = jstransformer(tr);
+  } catch (ex) {}
+  if (tr) {
+    // TODO: we may want to add a way for people to separately specify "locals"
+    var result = tr.render(str, options, options).body;
+    if (options && options.minify) {
+      try {
+        switch (tr.outputFormat) {
+          case 'js':
+            result = uglify.minify(result, {fromString: true}).code;
+            break;
+          case 'css':
+            result = new CleanCSS().minify(result).styles;
+            break;
+        }
+      } catch (ex) {
+        // better to fail to minify than output nothing
+      }
+    }
+    return result;
+  } else {
+    var err = new Error('unknown filter ":' + name + '"');
+    err.code = 'UNKNOWN_FILTER';
+    throw err;
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/cleancss b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/cleancss
new file mode 100644
index 0000000..7341641
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/cleancss
@@ -0,0 +1,15 @@
+#!/bin/sh
+basedir=`dirname "$0"`
+
+case `uname` in
+    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  "$basedir/node"  "$basedir/../clean-css/bin/cleancss" "$@"
+  ret=$?
+else 
+  node  "$basedir/../clean-css/bin/cleancss" "$@"
+  ret=$?
+fi
+exit $ret
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/cleancss.cmd b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/cleancss.cmd
new file mode 100644
index 0000000..ce4efa2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/cleancss.cmd
@@ -0,0 +1,7 @@
+@IF EXIST "%~dp0\node.exe" (
+  "%~dp0\node.exe"  "%~dp0\..\clean-css\bin\cleancss" %*
+) ELSE (
+  @SETLOCAL
+  @SET PATHEXT=%PATHEXT:;.JS;=;%
+  node  "%~dp0\..\clean-css\bin\cleancss" %*
+)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/uglifyjs b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/uglifyjs
new file mode 100644
index 0000000..27606b8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/uglifyjs
@@ -0,0 +1,15 @@
+#!/bin/sh
+basedir=`dirname "$0"`
+
+case `uname` in
+    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  "$basedir/node"  "$basedir/../uglify-js/bin/uglifyjs" "$@"
+  ret=$?
+else 
+  node  "$basedir/../uglify-js/bin/uglifyjs" "$@"
+  ret=$?
+fi
+exit $ret
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/uglifyjs.cmd b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/uglifyjs.cmd
new file mode 100644
index 0000000..3306013
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/.bin/uglifyjs.cmd
@@ -0,0 +1,7 @@
+@IF EXIST "%~dp0\node.exe" (
+  "%~dp0\node.exe"  "%~dp0\..\uglify-js\bin\uglifyjs" %*
+) ELSE (
+  @SETLOCAL
+  @SET PATHEXT=%PATHEXT:;.JS;=;%
+  node  "%~dp0\..\uglify-js\bin\uglifyjs" %*
+)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/History.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/History.md
new file mode 100644
index 0000000..c470d12
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/History.md
@@ -0,0 +1,975 @@
+[3.4.1 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.0...v3.4.1)
+==================
+
+* Fixed issue [#652](https://github.com/jakubpawlowicz/clean-css/issues/652) - order of restoring and removing tokens.
+
+[3.4.0 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.10...v3.4.0)
+==================
+
+* Adds an option for a fine-grained `@import` control.
+* Adds unit compatibility switches to disable length optimizations.
+* Adds inferring proxy settings from HTTP_PROXY environment variable.
+* Adds support for Polymer / Web Components special selectors.
+* Adds support for Polymer mixins.
+* Adds testing source maps in batch mode.
+* Unifies wrappers for simple & advanced optimizations.
+* Fixed issue [#596](https://github.com/jakubpawlowicz/clean-css/issues/596) - support for !ie IE<8 hack.
+* Fixed issue [#599](https://github.com/jakubpawlowicz/clean-css/issues/599) - support for inlined source maps.
+* Fixed issue [#607](https://github.com/jakubpawlowicz/clean-css/issues/607) - adds better rule reordering.
+* Fixed issue [#612](https://github.com/jakubpawlowicz/clean-css/issues/612) - adds HTTP proxy support.
+* Fixed issue [#618](https://github.com/jakubpawlowicz/clean-css/issues/618) - adds safer function validation.
+* Fixed issue [#625](https://github.com/jakubpawlowicz/clean-css/issues/625) - adds length unit optimizations.
+* Fixed issue [#632](https://github.com/jakubpawlowicz/clean-css/issues/632) - adds disabling remote `import`s.
+* Fixed issue [#635](https://github.com/jakubpawlowicz/clean-css/issues/635) - adds safer `0%` optimizations.
+* Fixed issue [#644](https://github.com/jakubpawlowicz/clean-css/issues/644) - adds time unit optimizations.
+* Fixed issue [#645](https://github.com/jakubpawlowicz/clean-css/issues/645) - adds bottom to top `media` merging.
+* Fixed issue [#648](https://github.com/jakubpawlowicz/clean-css/issues/648) - adds property level at-rule support.
+
+[3.3.10 / 2015-08-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.9...v3.3.10)
+==================
+
+* Adds better comments + keepBreaks handling.
+* Adds better text normalizing in source maps mode.
+* Fixes non-adjacent optimizations for source maps.
+* Fixes removing unused items.
+* Improves `outline` break up with source maps.
+* Refixed issue [#629](https://github.com/jakubpawlowicz/clean-css/issues/629) - source maps & background shorthands.
+
+[3.3.9 / 2015-08-09](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.8...v3.3.9)
+==================
+
+* Fixed issue [#640](https://github.com/jakubpawlowicz/clean-css/issues/640) - URI processing regression.
+
+[3.3.8 / 2015-08-06](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.7...v3.3.8)
+==================
+
+* Fixed issue [#629](https://github.com/jakubpawlowicz/clean-css/issues/629) - source maps & background shorthands.
+* Fixed issue [#630](https://github.com/jakubpawlowicz/clean-css/issues/630) - vendor prefixed flex optimizations.
+* Fixed issue [#633](https://github.com/jakubpawlowicz/clean-css/issues/633) - handling data URI with brackets.
+* Fixed issue [#634](https://github.com/jakubpawlowicz/clean-css/issues/634) - merging :placeholder selectors.
+
+[3.3.7 / 2015-07-29](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.6...v3.3.7)
+==================
+
+* Fixed issue [#616](https://github.com/jakubpawlowicz/clean-css/issues/616) - ordering in restructuring.
+
+[3.3.6 / 2015-07-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.5...v3.3.6)
+==================
+
+* Fixed issue [#620](https://github.com/jakubpawlowicz/clean-css/issues/620) - `bold` style in font shorthands.
+
+[3.3.5 / 2015-07-01](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.4...v3.3.5)
+==================
+
+* Fixed issue [#608](https://github.com/jakubpawlowicz/clean-css/issues/608) - custom URI protocols handling.
+
+[3.3.4 / 2015-06-24](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.3...v3.3.4)
+==================
+
+* Fixed issue [#610](https://github.com/jakubpawlowicz/clean-css/issues/610) - `border:inherit` restoring.
+* Fixed issue [#611](https://github.com/jakubpawlowicz/clean-css/issues/611) - edge case in quote stripping.
+
+[3.3.3 / 2015-06-16](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.2...v3.3.3)
+==================
+
+* Fixed issue [#603](https://github.com/jakubpawlowicz/clean-css/issues/603) - IE suffix hack defaults to on.
+
+[3.3.2 / 2015-06-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.1...v3.3.2)
+==================
+
+* Fixed issue [#595](https://github.com/jakubpawlowicz/clean-css/issues/595) - more relaxed block matching.
+* Fixed issue [#601](https://github.com/jakubpawlowicz/clean-css/issues/601) - percentage minifying inside `flex`.
+* Fixed issue [#602](https://github.com/jakubpawlowicz/clean-css/issues/602) - backslash IE hacks after a space.
+
+[3.3.1 / 2015-06-02](https://github.com/jakubpawlowicz/clean-css/compare/v3.3.0...v3.3.1)
+==================
+
+* Fixed issue [#590](https://github.com/jakubpawlowicz/clean-css/issues/590) - edge case in `@import` processing.
+
+[3.3.0 / 2015-05-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.11...v3.3.0)
+==================
+
+* Cleans up url rebase code getting rid of unnecessary state.
+* Cleans up tokenizer code getting rid of unnecessary state.
+* Moves source maps tracker into lib/source-maps/track.
+* Moves tokenizer code into lib/tokenizer.
+* Moves URL scanner into lib/urls/reduce (was named incorrectly before).
+* Moves URL rebasing & rewriting into lib/urls.
+* Fixed issue [#375](https://github.com/jakubpawlowicz/clean-css/issues/375) - unit compatibility switches.
+* Fixed issue [#436](https://github.com/jakubpawlowicz/clean-css/issues/436) - refactors URI rewriting.
+* Fixed issue [#448](https://github.com/jakubpawlowicz/clean-css/issues/448) - rebasing no protocol URIs.
+* Fixed issue [#517](https://github.com/jakubpawlowicz/clean-css/issues/517) - turning off color optimizations.
+* Fixed issue [#542](https://github.com/jakubpawlowicz/clean-css/issues/542) - space after closing brace in IE.
+* Fixed issue [#562](https://github.com/jakubpawlowicz/clean-css/issues/562) - optimizing invalid color values.
+* Fixed issue [#563](https://github.com/jakubpawlowicz/clean-css/issues/563) - `background:inherit` restoring.
+* Fixed issue [#570](https://github.com/jakubpawlowicz/clean-css/issues/570) - rebasing "no-url()" imports.
+* Fixed issue [#574](https://github.com/jakubpawlowicz/clean-css/issues/574) - rewriting internal URLs.
+* Fixed issue [#575](https://github.com/jakubpawlowicz/clean-css/issues/575) - missing directory as a `target`.
+* Fixed issue [#577](https://github.com/jakubpawlowicz/clean-css/issues/577) - `background-clip` into shorthand.
+* Fixed issue [#579](https://github.com/jakubpawlowicz/clean-css/issues/579) - `background-origin` into shorthand.
+* Fixed issue [#580](https://github.com/jakubpawlowicz/clean-css/issues/580) - mixed `@import` processing.
+* Fixed issue [#582](https://github.com/jakubpawlowicz/clean-css/issues/582) - overriding with prefixed values.
+* Fixed issue [#583](https://github.com/jakubpawlowicz/clean-css/issues/583) - URL quoting for SVG data.
+* Fixed issue [#587](https://github.com/jakubpawlowicz/clean-css/issues/587) - too aggressive `border` reordering.
+
+[3.2.11 / 2015-05-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.10...v3.2.11)
+==================
+
+* Fixed issue [#563](https://github.com/jakubpawlowicz/clean-css/issues/563) - `background:inherit` restoring.
+* Fixed issue [#582](https://github.com/jakubpawlowicz/clean-css/issues/582) - overriding with prefixed values.
+* Fixed issue [#583](https://github.com/jakubpawlowicz/clean-css/issues/583) - URL quoting for SVG data.
+* Fixed issue [#587](https://github.com/jakubpawlowicz/clean-css/issues/587) - too aggressive `border` reordering.
+
+[3.2.10 / 2015-05-14](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.9...v3.2.10)
+==================
+
+* Fixed issue [#572](https://github.com/jakubpawlowicz/clean-css/issues/572) - empty elements removal.
+
+[3.2.9 / 2015-05-08](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.8...v3.2.9)
+==================
+
+* Fixed issue [#567](https://github.com/jakubpawlowicz/clean-css/issues/567) - merging colors as functions.
+
+[3.2.8 / 2015-05-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.7...v3.2.8)
+==================
+
+* Fixed issue [#561](https://github.com/jakubpawlowicz/clean-css/issues/561) - restructuring special selectors.
+
+[3.2.7 / 2015-05-03](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.6...v3.2.7)
+==================
+
+* Fixed issue [#551](https://github.com/jakubpawlowicz/clean-css/issues/551) - edge case in restructuring.
+* Fixed issue [#553](https://github.com/jakubpawlowicz/clean-css/issues/553) - another style of SVG fallback.
+* Fixed issue [#558](https://github.com/jakubpawlowicz/clean-css/issues/558) - units in same selector merging.
+
+[3.2.6 / 2015-04-28](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.5...v3.2.6)
+==================
+
+* Fixed issue [#550](https://github.com/jakubpawlowicz/clean-css/issues/550) - proper `contentSources` tracking.
+* Fixed issue [#556](https://github.com/jakubpawlowicz/clean-css/issues/556) - regression in IE backslash hacks.
+
+[3.2.5 / 2015-04-25](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.4...v3.2.5)
+==================
+
+* Fixed issue [#543](https://github.com/jakubpawlowicz/clean-css/issues/543) - better "comment in body" handling.
+* Fixed issue [#548](https://github.com/jakubpawlowicz/clean-css/issues/548) - regression in font minifying.
+* Fixed issue [#549](https://github.com/jakubpawlowicz/clean-css/issues/549) - special comments in source maps.
+
+[3.2.4 / 2015-04-24](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.3...v3.2.4)
+==================
+
+* Fixed issue [#544](https://github.com/jakubpawlowicz/clean-css/issues/544) - regression in same value merging.
+* Fixed issue [#546](https://github.com/jakubpawlowicz/clean-css/issues/546) - IE<11 `calc()` issue.
+
+[3.2.3 / 2015-04-22](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.2...v3.2.3)
+==================
+
+* Fixed issue [#541](https://github.com/jakubpawlowicz/clean-css/issues/541) - `outline-style:auto` in shorthand.
+
+[3.2.2 / 2015-04-21](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.1...v3.2.2)
+==================
+
+* Fixed issue [#537](https://github.com/jakubpawlowicz/clean-css/issues/537) - regression in simple optimizer.
+
+[3.2.1 / 2015-04-20](https://github.com/jakubpawlowicz/clean-css/compare/v3.2.0...v3.2.1)
+==================
+
+* Fixed issue [#534](https://github.com/jakubpawlowicz/clean-css/issues/534) - wrong `@font-face` stringifying.
+
+[3.2.0 / 2015-04-19](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.9...v3.2.0)
+==================
+
+* Bumps commander to 2.8.x.
+* Fixes remote asset rebasing when passing data as a hash.
+* Improves path resolution inside source maps.
+* Makes `root` option implicitely default to `process.cwd()`.
+* Fixed issue [#371](https://github.com/jakubpawlowicz/clean-css/issues/371) - `background` fallback with `none`.
+* Fixed issue [#376](https://github.com/jakubpawlowicz/clean-css/issues/376) - option to disable `0[unit]` -> `0`.
+* Fixed issue [#396](https://github.com/jakubpawlowicz/clean-css/issues/396) - better input source maps tracking.
+* Fixed issue [#397](https://github.com/jakubpawlowicz/clean-css/issues/397) - support for source map sources.
+* Fixed issue [#399](https://github.com/jakubpawlowicz/clean-css/issues/399) - support compacting with source maps.
+* Fixed issue [#429](https://github.com/jakubpawlowicz/clean-css/issues/429) - unifies data tokenization.
+* Fixed issue [#446](https://github.com/jakubpawlowicz/clean-css/issues/446) - `list-style` fuzzy matching.
+* Fixed issue [#468](https://github.com/jakubpawlowicz/clean-css/issues/468) - bumps `source-map` to 0.4.x.
+* Fixed issue [#480](https://github.com/jakubpawlowicz/clean-css/issues/480) - extracting uppercase property names.
+* Fixed issue [#487](https://github.com/jakubpawlowicz/clean-css/issues/487) - source map paths under Windows.
+* Fixed issue [#490](https://github.com/jakubpawlowicz/clean-css/issues/490) - vendor prefixed multivalue `background`.
+* Fixed issue [#500](https://github.com/jakubpawlowicz/clean-css/issues/500) - merging duplicate adjacent properties.
+* Fixed issue [#504](https://github.com/jakubpawlowicz/clean-css/issues/504) - keeping `url()` quotes.
+* Fixed issue [#507](https://github.com/jakubpawlowicz/clean-css/issues/507) - merging longhands into many shorthands.
+* Fixed issue [#508](https://github.com/jakubpawlowicz/clean-css/issues/508) - removing duplicate media queries.
+* Fixed issue [#521](https://github.com/jakubpawlowicz/clean-css/issues/521) - unit optimizations inside `calc()`.
+* Fixed issue [#524](https://github.com/jakubpawlowicz/clean-css/issues/524) - timeouts in `@import` inlining.
+* Fixed issue [#526](https://github.com/jakubpawlowicz/clean-css/issues/526) - shorthand overriding into a function.
+* Fixed issue [#528](https://github.com/jakubpawlowicz/clean-css/issues/528) - better support for IE<9 hacks.
+* Fixed issue [#529](https://github.com/jakubpawlowicz/clean-css/issues/529) - wrong font weight minification.
+
+[3.1.9 / 2015-04-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.8...v3.1.9)
+==================
+
+* Fixes issue [#511](https://github.com/jakubpawlowicz/clean-css/issues/511) - `)` advanced processing.
+
+[3.1.8 / 2015-03-17](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.7...v3.1.8)
+==================
+
+* Fixes issue [#498](https://github.com/jakubpawlowicz/clean-css/issues/498) - reordering and flexbox.
+* Fixes issue [#499](https://github.com/jakubpawlowicz/clean-css/issues/499) - too aggressive `-` removal.
+
+[3.1.7 / 2015-03-16](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.6...v3.1.7)
+==================
+
+* Backports fix to [#480](https://github.com/jakubpawlowicz/clean-css/issues/480) - reordering and uppercase properties.
+* Fixes issue [#496](https://github.com/jakubpawlowicz/clean-css/issues/496) - space after bracket removal.
+
+[3.1.6 / 2015-03-12](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.5...v3.1.6)
+==================
+
+* Fixes issue [#489](https://github.com/jakubpawlowicz/clean-css/issues/489) - `AlphaImageLoader` IE filter.
+
+[3.1.5 / 2015-03-06](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.4...v3.1.5)
+==================
+
+* Fixes issue [#483](https://github.com/jakubpawlowicz/clean-css/issues/483) - property order in restructuring.
+
+[3.1.4 / 2015-03-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.3...v3.1.4)
+==================
+
+* Fixes issue [#472](https://github.com/jakubpawlowicz/clean-css/issues/472) - broken function minification.
+* Fixes issue [#477](https://github.com/jakubpawlowicz/clean-css/issues/477) - `@import`s order in restructuring.
+* Fixes issue [#478](https://github.com/jakubpawlowicz/clean-css/issues/478) - ultimate fix to brace whitespace.
+
+[3.1.3 / 2015-03-03](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.2...v3.1.3)
+==================
+
+* Fixes issue [#464](https://github.com/jakubpawlowicz/clean-css/issues/464) - data URI with quoted braces.
+* Fixes issue [#475](https://github.com/jakubpawlowicz/clean-css/issues/475) - whitespace after closing brace.
+
+[3.1.2 / 2015-03-01](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.1...v3.1.2)
+==================
+
+* Refixed issue [#471](https://github.com/jakubpawlowicz/clean-css/issues/471) - correct order after restructuring.
+* Fixes issue [#466](https://github.com/jakubpawlowicz/clean-css/issues/466) - rebuilding background shorthand.
+* Fixes issue [#462](https://github.com/jakubpawlowicz/clean-css/issues/462) - escaped apostrophes in selectors.
+
+[3.1.1 / 2015-02-27](https://github.com/jakubpawlowicz/clean-css/compare/v3.1.0...v3.1.1)
+==================
+
+* Fixed issue [#469](https://github.com/jakubpawlowicz/clean-css/issues/469) - extracting broken property.
+* Fixed issue [#470](https://github.com/jakubpawlowicz/clean-css/issues/470) - negative padding removal.
+* Fixed issue [#471](https://github.com/jakubpawlowicz/clean-css/issues/471) - correct order after restructuring.
+
+[3.1.0 / 2015-02-26](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.10...v3.1.0)
+==================
+
+* Adds `0deg` to `0` minification where possible.
+* Adds better non-adjacent selector merging when body is the same.
+* Adds official support for node.js 0.12.
+* Adds official support for io.js 1.0.
+* Adds restructuring optimizations to reorganize selectors, which vastly improves minification.
+* Fixed issue [#158](https://github.com/jakubpawlowicz/clean-css/issues/158) - adds body-based selectors reduction.
+* Fixed issue [#182](https://github.com/jakubpawlowicz/clean-css/issues/182) - removing space after closing brace.
+* Fixed issue [#204](https://github.com/jakubpawlowicz/clean-css/issues/204) - `@media` merging.
+* Fixed issue [#351](https://github.com/jakubpawlowicz/clean-css/issues/351) - remote `@import`s after content.
+* Fixed issue [#357](https://github.com/jakubpawlowicz/clean-css/issues/357) - non-standard but valid URLs.
+* Fixed issue [#416](https://github.com/jakubpawlowicz/clean-css/issues/416) - accepts hash as `minify` argument.
+* Fixed issue [#419](https://github.com/jakubpawlowicz/clean-css/issues/419) - multiple input source maps.
+* Fixed issue [#435](https://github.com/jakubpawlowicz/clean-css/issues/435) - `background-clip` in shorthand.
+* Fixed issue [#439](https://github.com/jakubpawlowicz/clean-css/issues/439) - `background-origin` in shorthand.
+* Fixed issue [#442](https://github.com/jakubpawlowicz/clean-css/issues/442) - space before adjacent `nav`.
+* Fixed issue [#445](https://github.com/jakubpawlowicz/clean-css/issues/445) - regression issue in url processor.
+* Fixed issue [#449](https://github.com/jakubpawlowicz/clean-css/issues/449) - warns of missing close braces.
+* Fixed issue [#463](https://github.com/jakubpawlowicz/clean-css/issues/463) - relative remote `@import` URLs.
+
+[3.0.10 / 2015-02-07](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.9...v3.0.10)
+==================
+
+* Fixed issue [#453](https://github.com/jakubpawlowicz/clean-css/issues/453) - double `background-repeat`.
+* Fixed issue [#455](https://github.com/jakubpawlowicz/clean-css/issues/455) - property extracting regression.
+
+[3.0.9 / 2015-02-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.8...v3.0.9)
+==================
+
+* Fixed issue [#452](https://github.com/jakubpawlowicz/clean-css/issues/452) - regression in advanced merging.
+
+[3.0.8 / 2015-01-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.7...v3.0.8)
+==================
+
+* Fixed issue [#447](https://github.com/jakubpawlowicz/clean-css/issues/447) - `background-color` in shorthands.
+* Fixed issue [#450](https://github.com/jakubpawlowicz/clean-css/issues/450) - name to hex color converting.
+
+[3.0.7 / 2015-01-22](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.6...v3.0.7)
+==================
+
+* Fixed issue [#441](https://github.com/jakubpawlowicz/clean-css/issues/441) - hex to name color converting.
+
+[3.0.6 / 2015-01-20](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.5...v3.0.6)
+==================
+
+* Refixed issue [#414](https://github.com/jakubpawlowicz/clean-css/issues/414) - source maps position fallback.
+
+[3.0.5 / 2015-01-18](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.4...v3.0.5)
+==================
+
+* Fixed issue [#414](https://github.com/jakubpawlowicz/clean-css/issues/414) - source maps position fallback.
+* Fixed issue [#433](https://github.com/jakubpawlowicz/clean-css/issues/433) - meging `!important` in shorthands.
+
+[3.0.4 / 2015-01-11](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.3...v3.0.4)
+==================
+
+* Fixed issue [#314](https://github.com/jakubpawlowicz/clean-css/issues/314) - spaces inside `calc`.
+
+[3.0.3 / 2015-01-07](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.2...v3.0.3)
+==================
+
+* Just a version bump as npm incorrectly things 2.2.23 is the latest one.
+
+[3.0.2 / 2015-01-04](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.1...v3.0.2)
+==================
+
+* Fixed issue [#422](https://github.com/jakubpawlowicz/clean-css/issues/422) - handling `calc` as a unit.
+
+[3.0.1 / 2014-12-19](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.0...v3.0.1)
+==================
+
+* Fixed issue [#410](https://github.com/jakubpawlowicz/clean-css/issues/410) - advanced merging and comments.
+* Fixed issue [#411](https://github.com/jakubpawlowicz/clean-css/issues/411) - properties and important comments.
+
+[3.0.0 / 2014-12-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.22...v3.0.0)
+==================
+
+* Adds more granular control over compatibility settings.
+* Adds support for @counter-style at-rule.
+* Adds `--source-map`/`sourceMap` switch for building input's source map.
+* Adds `--skip-shorthand-compacting`/`shorthandComacting` option for disabling shorthand compacting.
+* Allows `target` option to be a path to a folder instead of a file.
+* Allows disabling rounding precision. By [@superlukas](https://github.com/superlukas).
+* Breaks 2.x compatibility for using CleanCSS as a function.
+* Changes `minify` method output to handle multiple outputs.
+* Reworks minification to tokenize first then minify.
+  See [changes](https://github.com/jakubpawlowicz/clean-css/compare/b06f37d...dd8c14a).
+* Removes support for node.js 0.8.x.
+* Renames `noAdvanced` option into `advanced`.
+* Renames `noAggressiveMerging` option into `aggressiveMerging`.
+* Renames `noRebase` option into `rebase`.
+* Speeds up advanced processing by shortening optimize loop.
+* Fixed issue [#125](https://github.com/jakubpawlowicz/clean-css/issues/125) - source maps!
+* Fixed issue [#344](https://github.com/jakubpawlowicz/clean-css/issues/344) - merging `background-size` into shorthand.
+* Fixed issue [#352](https://github.com/jakubpawlowicz/clean-css/issues/352) - honors rebasing in imported stylesheets.
+* Fixed issue [#360](https://github.com/jakubpawlowicz/clean-css/issues/360) - adds 7 extra CSS colors.
+* Fixed issue [#363](https://github.com/jakubpawlowicz/clean-css/issues/363) - `rem` units overriding `px`.
+* Fixed issue [#373](https://github.com/jakubpawlowicz/clean-css/issues/373) - proper `background` shorthand merging.
+* Fixed issue [#395](https://github.com/jakubpawlowicz/clean-css/issues/395) - unescaped brackets in data URIs.
+* Fixed issue [#398](https://github.com/jakubpawlowicz/clean-css/issues/398) - restoring important comments.
+* Fixed issue [#400](https://github.com/jakubpawlowicz/clean-css/issues/400) - API to accept an array of filenames.
+* Fixed issue [#403](https://github.com/jakubpawlowicz/clean-css/issues/403) - tracking input files in source maps.
+* Fixed issue [#404](https://github.com/jakubpawlowicz/clean-css/issues/404) - no state sharing in API.
+* Fixed issue [#405](https://github.com/jakubpawlowicz/clean-css/issues/405) - disables default `background-size` merging.
+* Refixed issue [#304](https://github.com/jakubpawlowicz/clean-css/issues/304) - `background-position` merging.
+
+[2.2.22 / 2014-12-13](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.21...v2.2.22)
+==================
+
+* Backports fix to issue [#304](https://github.com/jakubpawlowicz/clean-css/issues/304) - `background-position` merging.
+
+[2.2.21 / 2014-12-10](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.20...v2.2.21)
+==================
+
+* Backports fix to issue [#373](https://github.com/jakubpawlowicz/clean-css/issues/373) - `background` shorthand merging.
+
+[2.2.20 / 2014-12-02](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.19...v2.2.20)
+==================
+
+* Backports fix to issue [#390](https://github.com/jakubpawlowicz/clean-css/issues/390) - pseudo-class merging.
+
+[2.2.19 / 2014-11-20](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.18...v2.2.19)
+==================
+
+* Fixed issue [#385](https://github.com/jakubpawlowicz/clean-css/issues/385) - edge cases in processing cut off data.
+
+[2.2.18 / 2014-11-17](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.17...v2.2.18)
+==================
+
+* Fixed issue [#383](https://github.com/jakubpawlowicz/clean-css/issues/383) - rounding fractions once again.
+
+[2.2.17 / 2014-11-09](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.16...v2.2.17)
+==================
+
+* Fixed issue [#380](https://github.com/jakubpawlowicz/clean-css/issues/380) - rounding fractions to a whole number.
+
+[2.2.16 / 2014-09-16](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.15...v2.2.16)
+==================
+
+* Fixed issue [#359](https://github.com/jakubpawlowicz/clean-css/issues/359) - handling escaped double backslash.
+* Fixed issue [#358](https://github.com/jakubpawlowicz/clean-css/issues/358) - property merging in compatibility mode.
+* Fixed issue [#356](https://github.com/jakubpawlowicz/clean-css/issues/356) - preserving `*+html` hack.
+* Fixed issue [#354](https://github.com/jakubpawlowicz/clean-css/issues/354) - `!important` overriding in shorthands.
+
+[2.2.15 / 2014-09-01](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.14...v2.2.15)
+==================
+
+* Fixed issue [#343](https://github.com/jakubpawlowicz/clean-css/issues/343) - too aggressive `rgba`/`hsla` minification.
+* Fixed issue [#345](https://github.com/jakubpawlowicz/clean-css/issues/345) - URL rebasing for document relative ones.
+* Fixed issue [#346](https://github.com/jakubpawlowicz/clean-css/issues/346) - overriding `!important` by `!important`.
+* Fixed issue [#350](https://github.com/jakubpawlowicz/clean-css/issues/350) - edge cases in `@import` processing.
+
+[2.2.14 / 2014-08-25](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.13...v2.2.14)
+==================
+
+* Makes multival operations idempotent.
+* Fixed issue [#339](https://github.com/jakubpawlowicz/clean-css/issues/339) - skips invalid properties.
+* Fixed issue [#341](https://github.com/jakubpawlowicz/clean-css/issues/341) - ensure output is shorter than input.
+
+[2.2.13 / 2014-08-12](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.12...v2.2.13)
+==================
+
+* Fixed issue [#337](https://github.com/jakubpawlowicz/clean-css/issues/337) - handling component importance.
+
+[2.2.12 / 2014-08-02](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.11...v2.2.12)
+==================
+
+* Fixed issue with tokenizer removing first selector after an unknown @ rule.
+* Fixed issue [#329](https://github.com/jakubpawlowicz/clean-css/issues/329) - `font` shorthands incorrectly processed.
+* Fixed issue [#332](https://github.com/jakubpawlowicz/clean-css/issues/332) - `background` shorthand with colors.
+* Refixed issue [#325](https://github.com/jakubpawlowicz/clean-css/issues/325) - invalid charset declarations.
+
+[2.2.11 / 2014-07-28](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.10...v2.2.11)
+==================
+
+* Fixed issue [#326](https://github.com/jakubpawlowicz/clean-css/issues/326) - `background-size` regression.
+
+[2.2.10 / 2014-07-27](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.9...v2.2.10)
+==================
+
+* Improved performance of advanced mode validators.
+* Fixed issue [#307](https://github.com/jakubpawlowicz/clean-css/issues/307) - `background-color` in multiple backgrounds.
+* Fixed issue [#322](https://github.com/jakubpawlowicz/clean-css/issues/322) - adds `background-size` support.
+* Fixed issue [#323](https://github.com/jakubpawlowicz/clean-css/issues/323) - stripping variable references.
+* Fixed issue [#325](https://github.com/jakubpawlowicz/clean-css/issues/325) - removing invalid `@charset` declarations.
+
+[2.2.9 / 2014-07-23](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.8...v2.2.9)
+==================
+
+* Adds `background` normalization according to W3C spec.
+* Fixed issue [#316](https://github.com/jakubpawlowicz/clean-css/issues/316) - incorrect `background` processing.
+
+[2.2.8 / 2014-07-14](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.7...v2.2.8)
+==================
+
+* Fixed issue [#313](https://github.com/jakubpawlowicz/clean-css/issues/313) - processing comment marks in URLs.
+* Fixed issue [#315](https://github.com/jakubpawlowicz/clean-css/issues/315) - `rgba`/`hsla` -> `transparent` in gradients.
+
+[2.2.7 / 2014-07-10](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.6...v2.2.7)
+==================
+
+* Fixed issue [#304](https://github.com/jakubpawlowicz/clean-css/issues/304) - merging multiple backgrounds.
+* Fixed issue [#312](https://github.com/jakubpawlowicz/clean-css/issues/312) - merging with mixed repeat.
+
+[2.2.6 / 2014-07-05](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.5...v2.2.6)
+==================
+
+* Adds faster quote matching in QuoteScanner.
+* Improves QuoteScanner to handle comments correctly.
+* Fixed issue [#308](https://github.com/jakubpawlowicz/clean-css/issues/308) - parsing comments in quoted URLs.
+* Fixed issue [#311](https://github.com/jakubpawlowicz/clean-css/issues/311) - leading/trailing decimal points.
+
+[2.2.5 / 2014-06-29](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.4...v2.2.5)
+==================
+
+* Adds removing extra spaces around / in border-radius.
+* Adds replacing same horizontal & vertical value in border-radius.
+* Fixed issue [#305](https://github.com/jakubpawlowicz/clean-css/issues/305) - allows width keywords in `border-width`.
+
+[2.2.4 / 2014-06-27](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.3...v2.2.4)
+==================
+
+* Fixed issue [#301](https://github.com/jakubpawlowicz/clean-css/issues/301) - proper `border-radius` processing.
+* Fixed issue [#303](https://github.com/jakubpawlowicz/clean-css/issues/303) - correctly preserves viewport units.
+
+[2.2.3 / 2014-06-24](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.2...v2.2.3)
+==================
+
+* Fixed issue [#302](https://github.com/jakubpawlowicz/clean-css/issues/302) - handling of `outline-style: auto`.
+
+[2.2.2 / 2014-06-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.1...v2.2.2)
+==================
+
+* Fixed issue [#297](https://github.com/jakubpawlowicz/clean-css/issues/297) - `box-shadow` zeros minification.
+
+[2.2.1 / 2014-06-14](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.0...v2.2.1)
+==================
+
+* Fixes new property optimizer for 'none' values.
+* Fixed issue [#294](https://github.com/jakubpawlowicz/clean-css/issues/294) - space after `rgba`/`hsla` in IE<=11.
+
+[2.2.0 / 2014-06-11](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.8...v2.2.0)
+==================
+
+* Adds a better algorithm for quotation marks' removal.
+* Adds a better non-adjacent optimizer compatible with the upcoming new property optimizer.
+* Adds minifying remote files directly from CLI.
+* Adds `--rounding-precision` to control rounding precision.
+* Moves quotation matching into a `QuoteScanner` class.
+* Adds `npm run browserify` for creating embeddable version of clean-css.
+* Fixed list-style-* advanced processing.
+* Fixed issue [#134](https://github.com/jakubpawlowicz/clean-css/issues/134) - merges properties into shorthand form.
+* Fixed issue [#164](https://github.com/jakubpawlowicz/clean-css/issues/164) - removes default values if not needed.
+* Fixed issue [#168](https://github.com/jakubpawlowicz/clean-css/issues/168) - adds better property merging algorithm.
+* Fixed issue [#173](https://github.com/jakubpawlowicz/clean-css/issues/173) - merges same properties if grouped.
+* Fixed issue [#184](https://github.com/jakubpawlowicz/clean-css/issues/184) - uses `!important` for optimization opportunities.
+* Fixed issue [#190](https://github.com/jakubpawlowicz/clean-css/issues/190) - uses shorthand to override another shorthand.
+* Fixed issue [#197](https://github.com/jakubpawlowicz/clean-css/issues/197) - adds borders merging by understandability.
+* Fixed issue [#210](https://github.com/jakubpawlowicz/clean-css/issues/210) - adds temporary workaround for aggressive merging.
+* Fixed issue [#246](https://github.com/jakubpawlowicz/clean-css/issues/246) - removes IE hacks when not in compatibility mode.
+* Fixed issue [#247](https://github.com/jakubpawlowicz/clean-css/issues/247) - removes deprecated `selectorsMergeMode` switch.
+* Refixed issue [#250](https://github.com/jakubpawlowicz/clean-css/issues/250) - based on new quotation marks removal.
+* Fixed issue [#257](https://github.com/jakubpawlowicz/clean-css/issues/257) - turns `rgba`/`hsla` to `transparent` if possible.
+* Fixed issue [#265](https://github.com/jakubpawlowicz/clean-css/issues/265) - adds support for multiple input files.
+* Fixed issue [#275](https://github.com/jakubpawlowicz/clean-css/issues/275) - handling transform properties.
+* Fixed issue [#276](https://github.com/jakubpawlowicz/clean-css/issues/276) - corrects unicode handling.
+* Fixed issue [#288](https://github.com/jakubpawlowicz/clean-css/issues/288) - adds smarter expression parsing.
+* Fixed issue [#293](https://github.com/jakubpawlowicz/clean-css/issues/293) - handles escaped `@` symbols in class names and IDs.
+
+[2.1.8 / 2014-03-28](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.7...v2.1.8)
+==================
+
+* Fixed issue [#267](https://github.com/jakubpawlowicz/clean-css/issues/267) - incorrect non-adjacent selector merging.
+
+[2.1.7 / 2014-03-24](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.6...v2.1.7)
+==================
+
+* Fixed issue [#264](https://github.com/jakubpawlowicz/clean-css/issues/264) - `@import` statements inside comments.
+
+[2.1.6 / 2014-03-10](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.5...v2.1.6)
+==================
+
+* Fixed issue [#258](https://github.com/jakubpawlowicz/clean-css/issues/258) - wrong `@import` handling in `EmptyRemoval`.
+
+[2.1.5 / 2014-03-07](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.4...v2.1.5)
+==================
+
+* Fixed issue [#255](https://github.com/jakubpawlowicz/clean-css/issues/255) - incorrect processing of a trailing `-0`.
+
+[2.1.4 / 2014-03-01](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.3...v2.1.4)
+==================
+
+* Fixed issue [#250](https://github.com/jakubpawlowicz/clean-css/issues/250) - correctly handle JSON data in quotations.
+
+[2.1.3 / 2014-02-26](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.2...v2.1.3)
+==================
+
+* Fixed issue [#248](https://github.com/jakubpawlowicz/clean-css/issues/248) - incorrect merging for vendor selectors.
+
+[2.1.2 / 2014-02-25](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.1...v2.1.2)
+==================
+
+* Fixed issue [#245](https://github.com/jakubpawlowicz/clean-css/issues/245) - incorrect handling of backslash IE hack.
+
+[2.1.1 / 2014-02-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.1.0...v2.1.1)
+==================
+
+* Adds faster selectors processing in advanced optimizer.
+* Fixed issue [#241](https://github.com/jakubpawlowicz/clean-css/issues/241) - incorrect handling of `:not()` selectors.
+
+[2.1.0 / 2014-02-13](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.8...v2.1.0)
+==================
+
+* Adds an optional callback to minify method.
+* Deprecates `--selectors-merge-mode` / `selectorsMergeMode` in favor to `--compatibility` / `compatibility`.
+* Fixes debug mode stats for stylesheets using `@import` statements.
+* Skips empty removal if advanced processing is enabled.
+* Fixed issue [#85](https://github.com/jakubpawlowicz/clean-css/issues/85) - resolving protocol `@import`s.
+* Fixed issue [#160](https://github.com/jakubpawlowicz/clean-css/issues/160) - re-runs optimizer until a clean pass.
+* Fixed issue [#161](https://github.com/jakubpawlowicz/clean-css/issues/161) - improves tokenizer performance.
+* Fixed issue [#163](https://github.com/jakubpawlowicz/clean-css/issues/163) - round pixels to 2nd decimal place.
+* Fixed issue [#165](https://github.com/jakubpawlowicz/clean-css/issues/165) - extra space after trailing parenthesis.
+* Fixed issue [#186](https://github.com/jakubpawlowicz/clean-css/issues/186) - strip unit from `0rem`.
+* Fixed issue [#207](https://github.com/jakubpawlowicz/clean-css/issues/207) - bug in parsing protocol `@import`s.
+* Fixed issue [#213](https://github.com/jakubpawlowicz/clean-css/issues/213) - faster `rgb` to `hex` transforms.
+* Fixed issue [#215](https://github.com/jakubpawlowicz/clean-css/issues/215) - leading zeros in numerical values.
+* Fixed issue [#217](https://github.com/jakubpawlowicz/clean-css/issues/217) - whitespace inside attribute selectors and URLs.
+* Fixed issue [#218](https://github.com/jakubpawlowicz/clean-css/issues/218) - `@import` statements cleanup.
+* Fixed issue [#220](https://github.com/jakubpawlowicz/clean-css/issues/220) - selector between comments.
+* Fixed issue [#223](https://github.com/jakubpawlowicz/clean-css/issues/223) - two-pass adjacent selectors merging.
+* Fixed issue [#226](https://github.com/jakubpawlowicz/clean-css/issues/226) - don't minify `border:none` to `border:0`.
+* Fixed issue [#229](https://github.com/jakubpawlowicz/clean-css/issues/229) - improved processing of fraction numbers.
+* Fixed issue [#230](https://github.com/jakubpawlowicz/clean-css/issues/230) - better handling of zero values.
+* Fixed issue [#235](https://github.com/jakubpawlowicz/clean-css/issues/235) - IE7 compatibility mode.
+* Fixed issue [#236](https://github.com/jakubpawlowicz/clean-css/issues/236) - incorrect rebasing with nested `import`s.
+
+[2.0.8 / 2014-02-07](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.7...v2.0.8)
+==================
+
+* Fixed issue [#232](https://github.com/jakubpawlowicz/clean-css/issues/232) - edge case in non-adjacent selectors merging.
+
+[2.0.7 / 2014-01-16](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.6...v2.0.7)
+==================
+
+* Fixed issue [#208](https://github.com/jakubpawlowicz/clean-css/issues/208) - don't swallow `@page` and `@viewport`.
+
+[2.0.6 / 2014-01-04](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.5...v2.0.6)
+==================
+
+* Fixed issue [#198](https://github.com/jakubpawlowicz/clean-css/issues/198) - process comments and `@import`s correctly.
+* Fixed issue [#205](https://github.com/jakubpawlowicz/clean-css/issues/205) - freeze on broken `@import` declaration.
+
+[2.0.5 / 2014-01-03](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.4...v2.0.5)
+==================
+
+* Fixed issue [#199](https://github.com/jakubpawlowicz/clean-css/issues/199) - keep line breaks with no advanced optimizations.
+* Fixed issue [#203](https://github.com/jakubpawlowicz/clean-css/issues/203) - Buffer as a first argument to minify method.
+
+[2.0.4 / 2013-12-19](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.3...v2.0.4)
+==================
+
+* Fixed issue [#193](https://github.com/jakubpawlowicz/clean-css/issues/193) - HSL color space normalization.
+
+[2.0.3 / 2013-12-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.2...v2.0.3)
+==================
+
+* Fixed issue [#191](https://github.com/jakubpawlowicz/clean-css/issues/191) - leading numbers in `font`/`animation` names.
+* Fixed issue [#192](https://github.com/jakubpawlowicz/clean-css/issues/192) - many `@import`s inside a comment.
+
+[2.0.2 / 2013-11-18](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.1...v2.0.2)
+==================
+
+* Fixed issue [#177](https://github.com/jakubpawlowicz/clean-css/issues/177) - process broken content correctly.
+
+[2.0.1 / 2013-11-14](https://github.com/jakubpawlowicz/clean-css/compare/v2.0.0...v2.0.1)
+==================
+
+* Fixed issue [#176](https://github.com/jakubpawlowicz/clean-css/issues/176) - hangs on `undefined` keyword.
+
+[2.0.0 / 2013-11-04](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.7...v2.0.0)
+==================
+
+* Adds simplified and more advanced text escaping / restoring via `EscapeStore` class.
+* Adds simplified and much faster empty elements removal.
+* Adds missing `@import` processing to our benchmark (run via `npm run bench`).
+* Adds CSS tokenizer which will make it possible to optimize content by reordering and/or merging selectors.
+* Adds basic optimizer removing duplicate selectors from a list.
+* Adds merging duplicate properties within a single selector's body.
+* Adds merging adjacent selectors within a scope (single and multiple ones).
+* Changes behavior of `--keep-line-breaks`/`keepBreaks` option to keep breaks after trailing braces only.
+* Makes all multiple selectors ordered alphabetically (aids merging).
+* Adds property overriding so more coarse properties override more granular ones.
+* Adds reducing non-adjacent selectors.
+* Adds `--skip-advanced`/`noAdvanced` switch to disable advanced optimizations.
+* Adds reducing non-adjacent selectors when overridden by more complex selectors.
+* Fixed issue [#138](https://github.com/jakubpawlowicz/clean-css/issues/138) - makes CleanCSS interface OO.
+* Fixed issue [#139](https://github.com/jakubpawlowicz/clean-css/issues/138) - consistent error & warning handling.
+* Fixed issue [#145](https://github.com/jakubpawlowicz/clean-css/issues/145) - debug mode in library too.
+* Fixed issue [#157](https://github.com/jakubpawlowicz/clean-css/issues/157) - gets rid of `removeEmpty` option.
+* Fixed issue [#159](https://github.com/jakubpawlowicz/clean-css/issues/159) - escaped quotes inside content.
+* Fixed issue [#162](https://github.com/jakubpawlowicz/clean-css/issues/162) - strip quotes from Base64 encoded URLs.
+* Fixed issue [#166](https://github.com/jakubpawlowicz/clean-css/issues/166) - `debug` formatting in CLI
+* Fixed issue [#167](https://github.com/jakubpawlowicz/clean-css/issues/167) - `background:transparent` minification.
+
+[1.1.7 / 2013-10-28](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.6...v1.1.7)
+==================
+
+* Fixed issue [#156](https://github.com/jakubpawlowicz/clean-css/issues/156) - `@import`s inside comments.
+
+[1.1.6 / 2013-10-26](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.5...v1.1.6)
+==================
+
+* Fixed issue [#155](https://github.com/jakubpawlowicz/clean-css/issues/155) - broken irregular CSS content.
+
+[1.1.5 / 2013-10-24](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.4...v1.1.5)
+==================
+
+* Fixed issue [#153](https://github.com/jakubpawlowicz/clean-css/issues/153) - `keepSpecialComments` `0`/`1` as a string.
+
+[1.1.4 / 2013-10-23](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.3...v1.1.4)
+==================
+
+* Fixed issue [#152](https://github.com/jakubpawlowicz/clean-css/issues/152) - adds an option to disable rebasing.
+
+[1.1.3 / 2013-10-04](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.2...v1.1.3)
+==================
+
+* Fixed issue [#150](https://github.com/jakubpawlowicz/clean-css/issues/150) - minifying `background:none`.
+
+[1.1.2 / 2013-09-29](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.1...v1.1.2)
+==================
+
+* Fixed issue [#149](https://github.com/jakubpawlowicz/clean-css/issues/149) - shorthand `font` property.
+
+[1.1.1 / 2013-09-07](https://github.com/jakubpawlowicz/clean-css/compare/v1.1.0...v1.1.1)
+==================
+
+* Fixed issue [#144](https://github.com/jakubpawlowicz/clean-css/issues/144) - skip URLs rebasing by default.
+
+[1.1.0 / 2013-09-06](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.12...v1.1.0)
+==================
+
+* Renamed lib's `debug` option to `benchmark` when doing per-minification benchmarking.
+* Added simplified comments processing & imports.
+* Fixed issue [#43](https://github.com/jakubpawlowicz/clean-css/issues/43) - `--debug` switch for minification stats.
+* Fixed issue [#65](https://github.com/jakubpawlowicz/clean-css/issues/65) - full color name / hex shortening.
+* Fixed issue [#84](https://github.com/jakubpawlowicz/clean-css/issues/84) - support for `@import` with media queries.
+* Fixed issue [#124](https://github.com/jakubpawlowicz/clean-css/issues/124) - raise error on broken imports.
+* Fixed issue [#126](https://github.com/jakubpawlowicz/clean-css/issues/126) - proper CSS expressions handling.
+* Fixed issue [#129](https://github.com/jakubpawlowicz/clean-css/issues/129) - rebasing imported URLs.
+* Fixed issue [#130](https://github.com/jakubpawlowicz/clean-css/issues/130) - better code modularity.
+* Fixed issue [#135](https://github.com/jakubpawlowicz/clean-css/issues/135) - require node.js 0.8+.
+
+[1.0.12 / 2013-07-19](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.11...v1.0.12)
+===================
+
+* Fixed issue [#121](https://github.com/jakubpawlowicz/clean-css/issues/121) - ability to skip `@import` processing.
+
+[1.0.11 / 2013-07-08](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.10...v1.0.11)
+===================
+
+* Fixed issue [#117](https://github.com/jakubpawlowicz/clean-css/issues/117) - line break escaping in comments.
+
+[1.0.10 / 2013-06-13](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.9...v1.0.10)
+===================
+
+* Fixed issue [#114](https://github.com/jakubpawlowicz/clean-css/issues/114) - comments in imported stylesheets.
+
+[1.0.9 / 2013-06-11](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.8...v1.0.9)
+==================
+
+* Fixed issue [#113](https://github.com/jakubpawlowicz/clean-css/issues/113) - `@import` in comments.
+
+[1.0.8 / 2013-06-10](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.7...v1.0.8)
+==================
+
+* Fixed issue [#112](https://github.com/jakubpawlowicz/clean-css/issues/112) - reducing `box-shadow` zeros.
+
+[1.0.7 / 2013-06-05](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.6...v1.0.7)
+==================
+
+* Support for `@import` URLs starting with `//`. By [@petetak](https://github.com/petetak).
+
+[1.0.6 / 2013-06-04](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.5...v1.0.6)
+==================
+
+* Fixed issue [#110](https://github.com/jakubpawlowicz/clean-css/issues/110) - data URIs in URLs.
+
+[1.0.5 / 2013-05-26](https://github.com/jakubpawlowicz/clean-css/compare/v1.0.4...v1.0.5)
+==================
+
+* Fixed issue [#107](https://github.com/jakubpawlowicz/clean-css/issues/107) - data URIs in imported stylesheets.
+
+1.0.4 / 2013-05-23
+==================
+
+* Rewrite relative URLs in imported stylesheets. By [@bluej100](https://github.com/bluej100).
+
+1.0.3 / 2013-05-20
+==================
+
+* Support alternative `@import` syntax with file name not wrapped inside `url()` statement.
+  By [@bluej100](https://github.com/bluej100).
+
+1.0.2 / 2013-04-29
+==================
+
+* Fixed issue [#97](https://github.com/jakubpawlowicz/clean-css/issues/97) - `--remove-empty` & FontAwesome.
+
+1.0.1 / 2013-04-08
+==================
+
+* Do not pick up `bench` and `test` while building `npm` package.
+  By [@sindresorhus](https://https://github.com/sindresorhus).
+
+1.0.0 / 2013-03-30
+==================
+
+* Fixed issue [#2](https://github.com/jakubpawlowicz/clean-css/issues/2) - resolving `@import` rules.
+* Fixed issue [#44](https://github.com/jakubpawlowicz/clean-css/issues/44) - examples in `--help`.
+* Fixed issue [#46](https://github.com/jakubpawlowicz/clean-css/issues/46) - preserving special characters in URLs and attributes.
+* Fixed issue [#80](https://github.com/jakubpawlowicz/clean-css/issues/80) - quotation in multi line strings.
+* Fixed issue [#83](https://github.com/jakubpawlowicz/clean-css/issues/83) - HSL to hex color conversions.
+* Fixed issue [#86](https://github.com/jakubpawlowicz/clean-css/issues/86) - broken `@charset` replacing.
+* Fixed issue [#88](https://github.com/jakubpawlowicz/clean-css/issues/88) - removes space in `! important`.
+* Fixed issue [#92](https://github.com/jakubpawlowicz/clean-css/issues/92) - uppercase hex to short versions.
+
+0.10.2 / 2013-03-19
+===================
+
+* Fixed issue [#79](https://github.com/jakubpawlowicz/clean-css/issues/79) - node.js 0.10.x compatibility.
+
+0.10.1 / 2013-02-14
+===================
+
+* Fixed issue [#66](https://github.com/jakubpawlowicz/clean-css/issues/66) - line breaks without extra spaces should
+  be handled correctly.
+
+0.10.0 / 2013-02-09
+===================
+
+* Switched from [optimist](https://github.com/substack/node-optimist) to
+  [commander](https://github.com/visionmedia/commander.js) for CLI processing.
+* Changed long options from `--removeempty` to `--remove-empty` and from `--keeplinebreaks` to `--keep-line-breaks`.
+* Fixed performance issue with replacing multiple `@charset` declarations and issue
+  with line break after `@charset` when using `keepLineBreaks` option. By [@rrjaime](https://github.com/rrjamie).
+* Removed Makefile in favor to `npm run` commands (e.g. `make check` -> `npm run check`).
+* Fixed issue [#47](https://github.com/jakubpawlowicz/clean-css/issues/47) - commandline issues on Windows.
+* Fixed issue [#49](https://github.com/jakubpawlowicz/clean-css/issues/49) - remove empty selectors from media query.
+* Fixed issue [#52](https://github.com/jakubpawlowicz/clean-css/issues/52) - strip fraction zeros if not needed.
+* Fixed issue [#58](https://github.com/jakubpawlowicz/clean-css/issues/58) - remove colon where possible.
+* Fixed issue [#59](https://github.com/jakubpawlowicz/clean-css/issues/59) - content property handling.
+
+0.9.1 / 2012-12-19
+==================
+
+* Fixed issue [#37](https://github.com/jakubpawlowicz/clean-css/issues/37) - converting
+  `white` and other colors in class names (reported by [@malgorithms](https://github.com/malgorithms)).
+
+0.9.0 / 2012-12-15
+==================
+
+* Added stripping quotation from font names (if possible).
+* Added stripping quotation from `@keyframes` declaration, `animation` and
+  `animation-name` property.
+* Added stripping quotations from attributes' value (e.g. `[data-target='x']`).
+* Added better hex->name and name->hex color shortening.
+* Added `font: normal` and `font: bold` shortening the same way as `font-weight` is.
+* Refactored shorthand selectors and added `border-radius`, `border-style`
+  and `border-color` shortening.
+* Added `margin`, `padding` and `border-width` shortening.
+* Added removing line break after commas.
+* Fixed removing whitespace inside media query definition.
+* Added removing line breaks after a comma, so all declarations are one-liners now.
+* Speed optimizations (~10% despite many new features).
+* Added [JSHint](https://github.com/jshint/jshint/) validation rules via `make check`.
+
+0.8.3 / 2012-11-29
+==================
+
+* Fixed HSL/HSLA colors processing.
+
+0.8.2 / 2012-10-31
+==================
+
+* Fixed shortening hex colors and their relation to hashes in URLs.
+* Cleanup by [@XhmikosR](https://github.com/XhmikosR).
+
+0.8.1 / 2012-10-28
+==================
+
+* Added better zeros processing for `rect(...)` syntax (clip property).
+
+0.8.0 / 2012-10-21
+==================
+
+* Added removing URLs quotation if possible.
+* Rewrote breaks processing.
+* Added `keepBreaks`/`-b` option to keep line breaks in the minimized file.
+* Reformatted [lib/clean.js](/lib/clean.js) so it's easier to follow the rules.
+* Minimized test data is now minimized with line breaks so it's easier to
+  compare the changes line by line.
+
+0.7.0 / 2012-10-14
+==================
+
+* Added stripping special comments to CLI (`--s0` and `--s1` options).
+* Added stripping special comments to programmatic interface
+  (`keepSpecialComments` option).
+
+0.6.0 / 2012-08-05
+==================
+
+* Full Windows support with tests (./test.bat).
+
+0.5.0 / 2012-08-02
+==================
+
+* Made path to vows local.
+* Explicit node.js 0.6 requirement.
+
+0.4.2 / 2012-06-28
+==================
+
+* Updated binary `-v` option (version).
+* Updated binary to output help when no options given (but not in piped mode).
+* Added binary tests.
+
+0.4.1 / 2012-06-10
+==================
+
+* Fixed stateless mode where calling `CleanCSS#process` directly was giving
+  errors (reported by [@facelessuser](https://github.com/facelessuser)).
+
+0.4.0 / 2012-06-04
+==================
+
+* Speed improvements up to 4x thanks to the rewrite of comments and CSS' content
+  processing.
+* Stripping empty CSS tags is now optional (see [bin/cleancss](/bin/cleancss) for details).
+* Improved debugging mode (see [test/bench.js](/test/bench.js))
+* Added `make bench` for a one-pass benchmark.
+
+0.3.3 / 2012-05-27
+==================
+
+* Fixed tests, [package.json](/package.json) for development, and regex
+  for removing empty declarations (thanks to [@vvo](https://github.com/vvo)).
+
+0.3.2 / 2012-01-17
+==================
+
+* Fixed output method under node.js 0.6 which incorrectly tried to close
+  `process.stdout`.
+
+0.3.1 / 2011-12-16
+==================
+
+* Fixed cleaning up `0 0 0 0` expressions.
+
+0.3.0 / 2011-11-29
+==================
+
+* Clean-css requires node.js 0.4.0+ to run.
+* Removed node.js's 0.2.x 'sys' package dependency
+  (thanks to [@jmalonzo](https://github.com/jmalonzo) for a patch).
+
+0.2.6 / 2011-11-27
+==================
+
+* Fixed expanding `+` signs in `calc()` when mixed up with adjacent `+` selector.
+
+0.2.5 / 2011-11-27
+==================
+
+* Fixed issue with cleaning up spaces inside `calc`/`-moz-calc` declarations
+  (thanks to [@cvan](https://github.com/cvan) for reporting it).
+* Fixed converting `#f00` to `red` in borders and gradients.
+
+0.2.4 / 2011-05-25
+==================
+
+* Fixed problem with expanding `none` to `0` in partial/full background
+  declarations.
+* Fixed including clean-css library from binary (global to local).
+
+0.2.3 / 2011-04-18
+==================
+
+* Fixed problem with optimizing IE filters.
+
+0.2.2 / 2011-04-17
+==================
+
+* Fixed problem with space before color in `border` property.
+
+0.2.1 / 2011-03-19
+==================
+
+* Added stripping space before `!important` keyword.
+* Updated repository location and author information in [package.json](/package.json).
+
+0.2.0 / 2011-03-02
+==================
+
+* Added options parsing via optimist.
+* Changed code inclusion (thus the version bump).
+
+0.1.0 / 2011-02-27
+==================
+
+* First version of clean-css library.
+* Implemented all basic CSS transformations.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/LICENSE
new file mode 100644
index 0000000..32bb13f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2015 JakubPawlowicz.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/README.md
new file mode 100644
index 0000000..05e2504
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/README.md
@@ -0,0 +1,369 @@
+[![NPM version](https://img.shields.io/npm/v/clean-css.svg?style=flat)](https://www.npmjs.com/package/clean-css)
+[![Linux Build Status](https://img.shields.io/travis/jakubpawlowicz/clean-css/master.svg?style=flat&label=Linux%20build)](https://travis-ci.org/jakubpawlowicz/clean-css)
+[![Windows Build status](https://img.shields.io/appveyor/ci/jakubpawlowicz/clean-css/master.svg?style=flat&label=Windows%20build)](https://ci.appveyor.com/project/jakubpawlowicz/clean-css/branch/master)
+[![Dependency Status](https://img.shields.io/david/jakubpawlowicz/clean-css.svg?style=flat)](https://david-dm.org/jakubpawlowicz/clean-css)
+[![devDependency Status](https://img.shields.io/david/dev/jakubpawlowicz/clean-css.svg?style=flat)](https://david-dm.org/jakubpawlowicz/clean-css#info=devDependencies)
+
+## What is clean-css?
+
+Clean-css is a fast and efficient [Node.js](http://nodejs.org/) library for minifying CSS files.
+
+According to [tests](http://goalsmashers.github.io/css-minification-benchmark/) it is one of the best available.
+
+
+## Usage
+
+### What are the requirements?
+
+```
+Node.js 0.10+ (tested on CentOS, Ubuntu, OS X 10.6+, and Windows 7+) or io.js 3.0+
+```
+
+### How to install clean-css?
+
+```
+npm install clean-css
+```
+
+### How to use clean-css CLI?
+
+Clean-css accepts the following command line arguments (please make sure
+you use `<source-file>` as the very last argument to avoid potential issues):
+
+```
+cleancss [options] source-file, [source-file, ...]
+
+-h, --help                     output usage information
+-v, --version                  output the version number
+-b, --keep-line-breaks         Keep line breaks
+-c, --compatibility [ie7|ie8]  Force compatibility mode (see Readme for advanced examples)
+-d, --debug                    Shows debug information (minification time & compression efficiency)
+-o, --output [output-file]     Use [output-file] as output instead of STDOUT
+-r, --root [root-path]         Set a root path to which resolve absolute @import rules
+-s, --skip-import              Disable @import processing
+-t, --timeout [seconds]        Per connection timeout when fetching remote @imports (defaults to 5 seconds)
+--rounding-precision [n]       Rounds to `N` decimal places. Defaults to 2. -1 disables rounding
+--s0                           Remove all special comments, i.e. /*! comment */
+--s1                           Remove all special comments but the first one
+--semantic-merging             Enables unsafe mode by assuming BEM-like semantic stylesheets (warning, this may break your styling!)
+--skip-advanced                Disable advanced optimizations - ruleset reordering & merging
+--skip-aggressive-merging      Disable properties merging based on their order
+--skip-import-from [rules]     Disable @import processing for specified rules
+--skip-media-merging           Disable @media merging
+--skip-rebase                  Disable URLs rebasing
+--skip-restructuring           Disable restructuring optimizations
+--skip-shorthand-compacting    Disable shorthand compacting
+--source-map                   Enables building input's source map
+--source-map-inline-sources    Enables inlining sources inside source maps
+```
+
+#### Examples:
+
+To minify a **public.css** file into **public-min.css** do:
+
+```
+cleancss -o public-min.css public.css
+```
+
+To minify the same **public.css** into the standard output skip the `-o` parameter:
+
+```
+cleancss public.css
+```
+
+More likely you would like to concatenate a couple of files.
+If you are on a Unix-like system:
+
+```bash
+cat one.css two.css three.css | cleancss -o merged-and-minified.css
+```
+
+On Windows:
+
+```bat
+type one.css two.css three.css | cleancss -o merged-and-minified.css
+```
+
+Or even gzip the result at once:
+
+```bash
+cat one.css two.css three.css | cleancss | gzip -9 -c > merged-minified-and-gzipped.css.gz
+```
+
+### How to use clean-css API?
+
+```js
+var CleanCSS = require('clean-css');
+var source = 'a{font-weight:bold;}';
+var minified = new CleanCSS().minify(source).styles;
+```
+
+CleanCSS constructor accepts a hash as a parameter, i.e.,
+`new CleanCSS(options)` with the following options available:
+
+* `advanced` - set to false to disable advanced optimizations - selector & property merging, reduction, etc.
+* `aggressiveMerging` - set to false to disable aggressive merging of properties.
+* `benchmark` - turns on benchmarking mode measuring time spent on cleaning up (run `npm run bench` to see example)
+* `compatibility` - enables compatibility mode, see [below for more examples](#how-to-set-compatibility-mode)
+* `debug` - set to true to get minification statistics under `stats` property (see `test/custom-test.js` for examples)
+* `inliner` - a hash of options for `@import` inliner, see [test/protocol-imports-test.js](https://github.com/jakubpawlowicz/clean-css/blob/master/test/protocol-imports-test.js#L372) for examples, or [this comment](https://github.com/jakubpawlowicz/clean-css/issues/612#issuecomment-119594185) for a proxy use case.
+* `keepBreaks` - whether to keep line breaks (default is false)
+* `keepSpecialComments` - `*` for keeping all (default), `1` for keeping first one only, `0` for removing all
+* `mediaMerging` - whether to merge `@media` at-rules (default is true)
+* `processImport` - whether to process `@import` rules
+* `processImportFrom` - a list of `@import` rules, can be `['all']` (default), `['local']`, `['remote']`, or a blacklisted path e.g. `['!fonts.googleapis.com']`
+* `rebase` - set to false to skip URL rebasing
+* `relativeTo` - path to **resolve** relative `@import` rules and URLs
+* `restructuring` - set to false to disable restructuring in advanced optimizations
+* `root` - path to **resolve** absolute `@import` rules and **rebase** relative URLs
+* `roundingPrecision` - rounding precision; defaults to `2`; `-1` disables rounding
+* `semanticMerging` - set to true to enable semantic merging mode which assumes BEM-like content (default is false as it's highly likely this will break your stylesheets - **use with caution**!)
+* `shorthandCompacting` - set to false to skip shorthand compacting (default is true unless sourceMap is set when it's false)
+* `sourceMap` - exposes source map under `sourceMap` property, e.g. `new CleanCSS().minify(source).sourceMap` (default is false)
+  If input styles are a product of CSS preprocessor (Less, Sass) an input source map can be passed as a string.
+* `sourceMapInlineSources` - set to true to inline sources inside a source map's `sourcesContent` field (defaults to false)
+  It is also required to process inlined sources from input source maps.
+* `target` - path to a folder or an output file to which **rebase** all URLs
+
+The output of `minify` method (or the 2nd argument to passed callback) is a hash containing the following fields:
+
+* `styles` - optimized output CSS as a string
+* `sourceMap` - output source map (if requested with `sourceMap` option)
+* `errors` - a list of errors raised
+* `warnings` - a list of warnings raised
+* `stats` - a hash of statistic information (if requested with `debug` option):
+  * `originalSize` - original content size (after import inlining)
+  * `minifiedSize` - optimized content size
+  * `timeSpent` - time spent on optimizations
+  * `efficiency` - a ratio of output size to input size (e.g. 25% if content was reduced from 100 bytes to 75 bytes)
+
+#### How to make sure remote `@import`s are processed correctly?
+
+In order to inline remote `@import` statements you need to provide a callback to minify method, e.g.:
+
+```js
+var CleanCSS = require('clean-css');
+var source = '@import url(http://path/to/remote/styles);';
+new CleanCSS().minify(source, function (errors, minified) {
+  // minified.styles
+});
+```
+
+This is due to a fact, that, while local files can be read synchronously, remote resources can only be processed asynchronously.
+If you don't provide a callback, then remote `@import`s will be left intact.
+
+### How to use clean-css with build tools?
+
+* [Broccoli](https://github.com/broccolijs/broccoli#broccoli): [broccoli-clean-css](https://github.com/shinnn/broccoli-clean-css)
+* [Brunch](http://brunch.io/): [clean-css-brunch](https://github.com/brunch/clean-css-brunch)
+* [Grunt](http://gruntjs.com): [grunt-contrib-cssmin](https://github.com/gruntjs/grunt-contrib-cssmin)
+* [Gulp](http://gulpjs.com/): [gulp-minify-css](https://github.com/jonathanepollack/gulp-minify-css)
+* [Gulp](http://gulpjs.com/): [using vinyl-map as a wrapper - courtesy of @sogko](https://github.com/jakubpawlowicz/clean-css/issues/342)
+* [component-builder2](https://github.com/component/builder2.js): [builder-clean-css](https://github.com/poying/builder-clean-css)
+* [Metalsmith](http://metalsmith.io): [metalsmith-clean-css](https://github.com/aymericbeaumet/metalsmith-clean-css)
+* [Lasso](https://github.com/lasso-js/lasso): [lasso-clean-css](https://github.com/yomed/lasso-clean-css)
+
+### What are the clean-css' dev commands?
+
+First clone the source, then run:
+
+* `npm run bench` for clean-css benchmarks (see [test/bench.js](https://github.com/jakubpawlowicz/clean-css/blob/master/test/bench.js) for details)
+* `npm run browserify` to create the browser-ready clean-css version
+* `npm run check` to check JS sources with [JSHint](https://github.com/jshint/jshint/)
+* `npm test` for the test suite
+
+## How to contribute to clean-css?
+
+See [CONTRIBUTING.md](https://github.com/jakubpawlowicz/clean-css/blob/master/CONTRIBUTING.md).
+
+## Tips & Tricks
+
+### How to preserve a comment block?
+
+Use the `/*!` notation instead of the standard one `/*`:
+
+```css
+/*!
+  Important comments included in minified output.
+*/
+```
+
+### How to rebase relative image URLs?
+
+Clean-css will handle it automatically for you (since version 1.1) in the following cases:
+
+* When using the CLI:
+  1. Use an output path via `-o`/`--output` to rebase URLs as relative to the output file.
+  2. Use a root path via `-r`/`--root` to rebase URLs as absolute from the given root path.
+  3. If you specify both then `-r`/`--root` takes precendence.
+* When using clean-css as a library:
+  1. Use a combination of `relativeTo` and `target` options for relative rebase (same as 1 in CLI).
+  2. Use a combination of `relativeTo` and `root` options for absolute rebase (same as 2 in CLI).
+  3. `root` takes precendence over `target` as in CLI.
+
+### How to generate source maps?
+
+Source maps are supported since version 3.0.
+
+Additionally to mapping original CSS files, clean-css also supports input source maps, so minified styles can be mapped into their [Less](http://lesscss.org/) or [Sass](http://sass-lang.com/) sources directly.
+
+Source maps are generated using [source-map](https://github.com/mozilla/source-map/) module from Mozilla.
+
+#### Using CLI
+
+To generate a source map, use `--source-map` switch, e.g.:
+
+```
+cleancss --source-map --output styles.min.css styles.css
+```
+
+Name of the output file is required, so a map file, named by adding `.map` suffix to output file name, can be created (styles.min.css.map in this case).
+
+#### Using API
+
+To generate a source map, use `sourceMap: true` option, e.g.:
+
+```js
+new CleanCSS({ sourceMap: true, target: pathToOutputDirectory })
+  .minify(source, function (minified) {
+    // access minified.sourceMap for SourceMapGenerator object
+    // see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
+    // see https://github.com/jakubpawlowicz/clean-css/blob/master/bin/cleancss#L114 on how it's used in clean-css' CLI
+});
+```
+
+Using API you can also pass an input source map directly:
+
+```js
+new CleanCSS({ sourceMap: inputSourceMapAsString, target: pathToOutputDirectory })
+  .minify(source, function (minified) {
+    // access minified.sourceMap to access SourceMapGenerator object
+    // see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
+    // see https://github.com/jakubpawlowicz/clean-css/blob/master/bin/cleancss#L114 on how it's used in clean-css' CLI
+});
+```
+
+Or even multiple input source maps at once (available since version 3.1):
+
+```js
+new CleanCSS({ sourceMap: true, target: pathToOutputDirectory }).minify({
+  'path/to/source/1': {
+    styles: '...styles...',
+    sourceMap: '...source-map...'
+  },
+  'path/to/source/2': {
+    styles: '...styles...',
+    sourceMap: '...source-map...'
+  }
+}, function (minified) {
+  // access minified.sourceMap as above
+});
+```
+
+### How to minify multiple files with API?
+
+#### Passing an array
+
+```js
+new CleanCSS().minify(['path/to/file/one', 'path/to/file/two']);
+```
+
+#### Passing a hash
+
+```js
+new CleanCSS().minify({
+  'path/to/file/one': {
+    styles: 'contents of file one'
+  },
+  'path/to/file/two': {
+    styles: 'contents of file two'
+  }
+});
+```
+
+### How to set a compatibility mode?
+
+Compatibility settings are controlled by `--compatibility` switch (CLI) and `compatibility` option (library mode).
+
+In both modes the following values are allowed:
+
+* `'ie7'` - Internet Explorer 7 compatibility mode
+* `'ie8'` - Internet Explorer 8 compatibility mode
+* `''` or `'*'` (default) - Internet Explorer 9+ compatibility mode
+
+Since clean-css 3 a fine grained control is available over
+[those settings](https://github.com/jakubpawlowicz/clean-css/blob/master/lib/utils/compatibility.js),
+with the following options available:
+
+* `'[+-]colors.opacity'` - - turn on (+) / off (-) `rgba()` / `hsla()` declarations removal
+* `'[+-]properties.backgroundClipMerging'` - turn on / off background-clip merging into shorthand
+* `'[+-]properties.backgroundOriginMerging'` - turn on / off background-origin merging into shorthand
+* `'[+-]properties.backgroundSizeMerging'` - turn on / off background-size merging into shorthand
+* `'[+-]properties.colors'` - turn on / off any color optimizations
+* `'[+-]properties.ieBangHack'` - turn on / off IE bang hack removal
+* `'[+-]properties.iePrefixHack'` - turn on / off IE prefix hack removal
+* `'[+-]properties.ieSuffixHack'` - turn on / off IE suffix hack removal
+* `'[+-]properties.merging'` - turn on / off property merging based on understandability
+* `'[+-]properties.spaceAfterClosingBrace'` - turn on / off removing space after closing brace - `url() no-repeat` into `url()no-repeat`
+* `'[+-]properties.urlQuotes'` - turn on / off `url()` quoting
+* `'[+-]properties.zeroUnits'` - turn on / off units removal after a `0` value
+* `'[+-]selectors.adjacentSpace'` - turn on / off extra space before `nav` element
+* `'[+-]selectors.ie7Hack'` - turn on / off IE7 selector hack removal (`*+html...`)
+* `'[+-]selectors.special'` - a regular expression with all special, unmergeable selectors (leave it empty unless you know what you are doing)
+* `'[+-]units.ch'` - turn on / off treating `ch` as a proper unit
+* `'[+-]units.in'` - turn on / off treating `in` as a proper unit
+* `'[+-]units.pc'` - turn on / off treating `pc` as a proper unit
+* `'[+-]units.pt'` - turn on / off treating `pt` as a proper unit
+* `'[+-]units.rem'` - turn on / off treating `rem` as a proper unit
+* `'[+-]units.vh'` - turn on / off treating `vh` as a proper unit
+* `'[+-]units.vm'` - turn on / off treating `vm` as a proper unit
+* `'[+-]units.vmax'` - turn on / off treating `vmax` as a proper unit
+* `'[+-]units.vmin'` - turn on / off treating `vmin` as a proper unit
+* `'[+-]units.vm'` - turn on / off treating `vm` as a proper unit
+
+For example, using `--compatibility 'ie8,+units.rem'` will ensure IE8 compatibility while enabling `rem` units so the following style `margin:0px 0rem` can be shortened to `margin:0`, while in pure IE8 mode it can't be.
+
+To pass a single off (-) switch in CLI please use the following syntax `--compatibility *,-units.rem`.
+
+In library mode you can also pass `compatibility` as a hash of options.
+
+### What advanced optimizations are applied?
+
+All advanced optimizations are dispatched [here](https://github.com/jakubpawlowicz/clean-css/blob/master/lib/selectors/advanced.js#L59), and this is what they do:
+
+* `recursivelyOptimizeBlocks` - does all the following operations on a block (think `@media` or `@keyframe` at-rules);
+* `recursivelyOptimizeProperties` - optimizes properties in rulesets and "flat at-rules" (like @font-face) by splitting them into components (e.g. `margin` into `margin-(*)`), optimizing, and rebuilding them back. You may want to use `shorthandCompacting` option to control whether you want to turn multiple (long-hand) properties into a shorthand ones;
+* `removeDuplicates` - gets rid of duplicate rulesets with exactly the same set of properties (think of including the same Sass / Less partial twice for no good reason);
+* `mergeAdjacent` - merges adjacent rulesets with the same selector or rules;
+* `reduceNonAdjacent` - identifies which properties are overridden in same-selector non-adjacent rulesets, and removes them;
+* `mergeNonAdjacentBySelector` - identifies same-selector non-adjacent rulesets which can be moved (!) to be merged, requires all intermediate rulesets to not redefine the moved properties, or if redefined to be either more coarse grained (e.g. `margin` vs `margin-top`) or have the same value;
+* `mergeNonAdjacentByBody` - same as the one above but for same-rules non-adjacent rulesets;
+* `restructure` - tries to reorganize different-selector different-rules rulesets so they take less space, e.g. `.one{padding:0}.two{margin:0}.one{margin-bottom:3px}` into `.two{margin:0}.one{padding:0;margin-bottom:3px}`;
+* `removeDuplicateMediaQueries` - removes duplicated `@media` at-rules;
+* `mergeMediaQueries` - merges non-adjacent `@media` at-rules by same rules as `mergeNonAdjacentBy*` above;
+
+## Acknowledgments (sorted alphabetically)
+
+* Anthony Barre ([@abarre](https://github.com/abarre)) for improvements to
+  `@import` processing, namely introducing the `--skip-import` /
+  `processImport` options.
+* Simon Altschuler ([@altschuler](https://github.com/altschuler)) for fixing
+  `@import` processing inside comments.
+* Isaac ([@facelessuser](https://github.com/facelessuser)) for pointing out
+  a flaw in clean-css' stateless mode.
+* Jan Michael Alonzo ([@jmalonzo](https://github.com/jmalonzo)) for a patch
+  removing node.js' old `sys` package.
+* Luke Page ([@lukeapage](https://github.com/lukeapage)) for suggestions and testing the source maps feature.
+  Plus everyone else involved in [#125](https://github.com/jakubpawlowicz/clean-css/issues/125) for pushing it forward.
+* Timur Kristóf ([@Venemo](https://github.com/Venemo)) for an outstanding
+  contribution of advanced property optimizer for 2.2 release.
+* Vincent Voyer ([@vvo](https://github.com/vvo)) for a patch with better
+  empty element regex and for inspiring us to do many performance improvements
+  in 0.4 release.
+* [@XhmikosR](https://github.com/XhmikosR) for suggesting new features
+  (option to remove special comments and strip out URLs quotation) and
+  pointing out numerous improvements (JSHint, media queries).
+
+## License
+
+Clean-css is released under the [MIT License](https://github.com/jakubpawlowicz/clean-css/blob/master/LICENSE).
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/bin/cleancss b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/bin/cleancss
new file mode 100644
index 0000000..5f95b7e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/bin/cleancss
@@ -0,0 +1,168 @@
+#!/usr/bin/env node
+
+var fs = require('fs');
+var path = require('path');
+var CleanCSS = require('../index');
+
+var commands = require('commander');
+
+var packageConfig = fs.readFileSync(path.join(path.dirname(fs.realpathSync(process.argv[1])), '../package.json'));
+var buildVersion = JSON.parse(packageConfig).version;
+
+var isWindows = process.platform == 'win32';
+
+// Specify commander options to parse command line params correctly
+commands
+  .version(buildVersion, '-v, --version')
+  .usage('[options] source-file, [source-file, ...]')
+  .option('-b, --keep-line-breaks', 'Keep line breaks')
+  .option('-c, --compatibility [ie7|ie8]', 'Force compatibility mode (see Readme for advanced examples)')
+  .option('-d, --debug', 'Shows debug information (minification time & compression efficiency)')
+  .option('-o, --output [output-file]', 'Use [output-file] as output instead of STDOUT')
+  .option('-r, --root [root-path]', 'Set a root path to which resolve absolute @import rules')
+  .option('-s, --skip-import', 'Disable @import processing')
+  .option('-t, --timeout [seconds]', 'Per connection timeout when fetching remote @imports (defaults to 5 seconds)')
+  .option('--rounding-precision [n]', 'Rounds to `N` decimal places. Defaults to 2. -1 disables rounding', parseInt)
+  .option('--s0', 'Remove all special comments, i.e. /*! comment */')
+  .option('--s1', 'Remove all special comments but the first one')
+  .option('--semantic-merging', 'Enables unsafe mode by assuming BEM-like semantic stylesheets (warning, this may break your styling!)')
+  .option('--skip-advanced', 'Disable advanced optimizations - ruleset reordering & merging')
+  .option('--skip-aggressive-merging', 'Disable properties merging based on their order')
+  .option('--skip-import-from [rules]', 'Disable @import processing for specified rules', function (val) { return val.split(','); }, [])
+  .option('--skip-media-merging', 'Disable @media merging')
+  .option('--skip-rebase', 'Disable URLs rebasing')
+  .option('--skip-restructuring', 'Disable restructuring optimizations')
+  .option('--skip-shorthand-compacting', 'Disable shorthand compacting')
+  .option('--source-map', 'Enables building input\'s source map')
+  .option('--source-map-inline-sources', 'Enables inlining sources inside source maps');
+
+commands.on('--help', function () {
+  console.log('  Examples:\n');
+  console.log('    %> cleancss one.css');
+  console.log('    %> cleancss -o one-min.css one.css');
+  if (isWindows) {
+    console.log('    %> type one.css two.css three.css | cleancss -o merged-and-minified.css');
+  } else {
+    console.log('    %> cat one.css two.css three.css | cleancss -o merged-and-minified.css');
+    console.log('    %> cat one.css two.css three.css | cleancss | gzip -9 -c > merged-minified-and-gzipped.css.gz');
+  }
+  console.log('');
+  process.exit();
+});
+
+commands.parse(process.argv);
+
+// If no sensible data passed in just print help and exit
+var fromStdin = !process.env.__DIRECT__ && !process.stdin.isTTY;
+if (!fromStdin && commands.args.length === 0) {
+  commands.outputHelp();
+  return 0;
+}
+
+// Now coerce commands into CleanCSS configuration...
+var options = {
+  advanced: commands.skipAdvanced ? false : true,
+  aggressiveMerging: commands.skipAggressiveMerging ? false : true,
+  compatibility: commands.compatibility,
+  debug: commands.debug,
+  inliner: commands.timeout ? { timeout: parseFloat(commands.timeout) * 1000 } : undefined,
+  keepBreaks: !!commands.keepLineBreaks,
+  keepSpecialComments: commands.s0 ? 0 : (commands.s1 ? 1 : '*'),
+  mediaMerging: commands.skipMediaMerging ? false : true,
+  processImport: commands.skipImport ? false : true,
+  processImportFrom: processImportFrom(commands.skipImportFrom),
+  rebase: commands.skipRebase ? false : true,
+  restructuring: commands.skipRestructuring ? false : true,
+  root: commands.root,
+  roundingPrecision: commands.roundingPrecision,
+  semanticMerging: commands.semanticMerging ? true : false,
+  shorthandCompacting: commands.skipShorthandCompacting ? false : true,
+  sourceMap: commands.sourceMap,
+  sourceMapInlineSources: commands.sourceMapInlineSources,
+  target: commands.output
+};
+
+if (options.root || commands.args.length > 0)
+  options.relativeTo = path.dirname(path.resolve(options.root || commands.args[0]));
+
+if (options.sourceMap && !options.target) {
+  outputFeedback(['Source maps will not be built because you have not specified an output file.'], true);
+  options.sourceMap = false;
+}
+
+// ... and do the magic!
+if (commands.args.length > 0) {
+  minify(commands.args);
+} else {
+  var stdin = process.openStdin();
+  stdin.setEncoding('utf-8');
+  var data = '';
+  stdin.on('data', function (chunk) {
+    data += chunk;
+  });
+  stdin.on('end', function () {
+    minify(data);
+  });
+}
+
+function processImportFrom(rules) {
+  if (rules.length === 0) {
+    return ['all'];
+  } else if (rules.length == 1 && rules[0] == 'all') {
+    return [];
+  } else {
+    return rules.map(function (rule) {
+      if (rule == 'local')
+        return 'remote';
+      else if (rule == 'remote')
+        return 'local';
+      else
+        return '!' + rule;
+    });
+  }
+}
+
+function minify(data) {
+  new CleanCSS(options).minify(data, function (errors, minified) {
+    if (options.debug) {
+      console.error('Original: %d bytes', minified.stats.originalSize);
+      console.error('Minified: %d bytes', minified.stats.minifiedSize);
+      console.error('Efficiency: %d%', ~~(minified.stats.efficiency * 10000) / 100.0);
+      console.error('Time spent: %dms', minified.stats.timeSpent);
+    }
+
+    outputFeedback(minified.errors, true);
+    outputFeedback(minified.warnings);
+
+    if (minified.errors.length > 0)
+      process.exit(1);
+
+    if (minified.sourceMap) {
+      var mapFilename = path.basename(options.target) + '.map';
+      output(minified.styles + '/*# sourceMappingURL=' + mapFilename + ' */');
+      outputMap(minified.sourceMap, mapFilename);
+    } else {
+      output(minified.styles);
+    }
+  });
+}
+
+function output(minified) {
+  if (options.target)
+    fs.writeFileSync(options.target, minified, 'utf8');
+  else
+    process.stdout.write(minified);
+}
+
+function outputMap(sourceMap, mapFilename) {
+  var mapPath = path.join(path.dirname(options.target), mapFilename);
+  fs.writeFileSync(mapPath, sourceMap.toString(), 'utf-8');
+}
+
+function outputFeedback(messages, isError) {
+  var prefix = isError ? '\x1B[31mERROR\x1B[39m:' : 'WARNING:';
+
+  messages.forEach(function (message) {
+    console.error('%s %s', prefix, message);
+  });
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/index.js
new file mode 100644
index 0000000..d7b0503
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/index.js
@@ -0,0 +1 @@
+module.exports = require('./lib/clean');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/clean.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/clean.js
new file mode 100644
index 0000000..ea65f9e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/clean.js
@@ -0,0 +1,231 @@
+/**
+ * Clean-css - https://github.com/jakubpawlowicz/clean-css
+ * Released under the terms of MIT license
+ *
+ * Copyright (C) 2015 JakubPawlowicz.com
+ */
+
+var ImportInliner = require('./imports/inliner');
+var rebaseUrls = require('./urls/rebase');
+
+var tokenize = require('./tokenizer/tokenize');
+var simpleOptimize = require('./selectors/simple');
+var advancedOptimize = require('./selectors/advanced');
+
+var simpleStringify = require('./stringifier/simple');
+var sourceMapStringify = require('./stringifier/source-maps');
+
+var CommentsProcessor = require('./text/comments-processor');
+var ExpressionsProcessor = require('./text/expressions-processor');
+var FreeTextProcessor = require('./text/free-text-processor');
+var UrlsProcessor = require('./text/urls-processor');
+
+var Compatibility = require('./utils/compatibility');
+var InputSourceMapTracker = require('./utils/input-source-map-tracker');
+var SourceTracker = require('./utils/source-tracker');
+var SourceReader = require('./utils/source-reader');
+var Validator = require('./properties/validator');
+
+var fs = require('fs');
+var path = require('path');
+var url = require('url');
+
+var override = require('./utils/object').override;
+
+var DEFAULT_TIMEOUT = 5000;
+
+var CleanCSS = module.exports = function CleanCSS(options) {
+  options = options || {};
+
+  this.options = {
+    advanced: undefined === options.advanced ? true : !!options.advanced,
+    aggressiveMerging: undefined === options.aggressiveMerging ? true : !!options.aggressiveMerging,
+    benchmark: options.benchmark,
+    compatibility: new Compatibility(options.compatibility).toOptions(),
+    debug: options.debug,
+    explicitRoot: !!options.root,
+    explicitTarget: !!options.target,
+    inliner: options.inliner || {},
+    keepBreaks: options.keepBreaks || false,
+    keepSpecialComments: 'keepSpecialComments' in options ? options.keepSpecialComments : '*',
+    mediaMerging: undefined === options.mediaMerging ? true : !!options.mediaMerging,
+    processImport: undefined === options.processImport ? true : !!options.processImport,
+    processImportFrom: importOptionsFrom(options.processImportFrom),
+    rebase: undefined === options.rebase ? true : !!options.rebase,
+    relativeTo: options.relativeTo,
+    restructuring: undefined === options.restructuring ? true : !!options.restructuring,
+    root: options.root || process.cwd(),
+    roundingPrecision: options.roundingPrecision,
+    semanticMerging: undefined === options.semanticMerging ? false : !!options.semanticMerging,
+    shorthandCompacting: undefined === options.shorthandCompacting ? true : !!options.shorthandCompacting,
+    sourceMap: options.sourceMap,
+    sourceMapInlineSources: !!options.sourceMapInlineSources,
+    target: !options.target || missingDirectory(options.target) || presentDirectory(options.target) ? options.target : path.dirname(options.target)
+  };
+
+  this.options.inliner.timeout = this.options.inliner.timeout || DEFAULT_TIMEOUT;
+  this.options.inliner.request = override(
+    /* jshint camelcase: false */
+    proxyOptionsFrom(process.env.HTTP_PROXY || process.env.http_proxy),
+    this.options.inliner.request || {}
+  );
+};
+
+function importOptionsFrom(rules) {
+  return undefined === rules ? ['all'] : rules;
+}
+
+function missingDirectory(filepath) {
+  return !fs.existsSync(filepath) && !/\.css$/.test(filepath);
+}
+
+function presentDirectory(filepath) {
+  return fs.existsSync(filepath) && fs.statSync(filepath).isDirectory();
+}
+
+function proxyOptionsFrom(httpProxy) {
+  return httpProxy ?
+    {
+      hostname: url.parse(httpProxy).hostname,
+      port: parseInt(url.parse(httpProxy).port)
+    } :
+    {};
+}
+
+CleanCSS.prototype.minify = function (data, callback) {
+  var context = {
+    stats: {},
+    errors: [],
+    warnings: [],
+    options: this.options,
+    debug: this.options.debug,
+    localOnly: !callback,
+    sourceTracker: new SourceTracker(),
+    validator: new Validator(this.options.compatibility)
+  };
+
+  if (context.options.sourceMap)
+    context.inputSourceMapTracker = new InputSourceMapTracker(context);
+
+  context.sourceReader = new SourceReader(context, data);
+  data = context.sourceReader.toString();
+
+  if (context.options.processImport || data.indexOf('@shallow') > 0) {
+    // inline all imports
+    var runner = callback ?
+      process.nextTick :
+      function (callback) { return callback(); };
+
+    return runner(function () {
+      return new ImportInliner(context).process(data, {
+        localOnly: context.localOnly,
+        imports: context.options.processImportFrom,
+        whenDone: runMinifier(callback, context)
+      });
+    });
+  } else {
+    return runMinifier(callback, context)(data);
+  }
+};
+
+function runMinifier(callback, context) {
+  function whenSourceMapReady (data) {
+    data = context.options.debug ?
+      minifyWithDebug(context, data) :
+      minify(context, data);
+    data = withMetadata(context, data);
+
+    return callback ?
+      callback.call(null, context.errors.length > 0 ? context.errors : null, data) :
+      data;
+  }
+
+  return function (data) {
+    if (context.options.sourceMap) {
+      return context.inputSourceMapTracker.track(data, function () {
+        if (context.options.sourceMapInlineSources) {
+          return context.inputSourceMapTracker.resolveSources(function () {
+            return whenSourceMapReady(data);
+          });
+        } else {
+          return whenSourceMapReady(data);
+        }
+      });
+    } else {
+      return whenSourceMapReady(data);
+    }
+  };
+}
+
+function withMetadata(context, data) {
+  data.stats = context.stats;
+  data.errors = context.errors;
+  data.warnings = context.warnings;
+  return data;
+}
+
+function minifyWithDebug(context, data) {
+  var startedAt = process.hrtime();
+  context.stats.originalSize = context.sourceTracker.removeAll(data).length;
+
+  data = minify(context, data);
+
+  var elapsed = process.hrtime(startedAt);
+  context.stats.timeSpent = ~~(elapsed[0] * 1e3 + elapsed[1] / 1e6);
+  context.stats.efficiency = 1 - data.styles.length / context.stats.originalSize;
+  context.stats.minifiedSize = data.styles.length;
+
+  return data;
+}
+
+function benchmark(runner) {
+  return function (processor, action) {
+    var name =  processor.constructor.name + '#' + action;
+    var start = process.hrtime();
+    runner(processor, action);
+    var itTook = process.hrtime(start);
+    console.log('%d ms: ' + name, 1000 * itTook[0] + itTook[1] / 1000000);
+  };
+}
+
+function minify(context, data) {
+  var options = context.options;
+
+  var commentsProcessor = new CommentsProcessor(context, options.keepSpecialComments, options.keepBreaks, options.sourceMap);
+  var expressionsProcessor = new ExpressionsProcessor(options.sourceMap);
+  var freeTextProcessor = new FreeTextProcessor(options.sourceMap);
+  var urlsProcessor = new UrlsProcessor(context, options.sourceMap, options.compatibility.properties.urlQuotes);
+
+  var stringify = options.sourceMap ? sourceMapStringify : simpleStringify;
+
+  var run = function (processor, action) {
+    data = typeof processor == 'function' ?
+      processor(data) :
+      processor[action](data);
+  };
+
+  if (options.benchmark)
+    run = benchmark(run);
+
+  run(commentsProcessor, 'escape');
+  run(expressionsProcessor, 'escape');
+  run(urlsProcessor, 'escape');
+  run(freeTextProcessor, 'escape');
+
+  function restoreEscapes(data, prefixContent) {
+    data = freeTextProcessor.restore(data, prefixContent);
+    data = urlsProcessor.restore(data);
+    data = options.rebase ? rebaseUrls(data, context) : data;
+    data = expressionsProcessor.restore(data);
+    return commentsProcessor.restore(data);
+  }
+
+  var tokens = tokenize(data, context);
+
+  simpleOptimize(tokens, options);
+
+  if (options.advanced)
+    advancedOptimize(tokens, options, context.validator, true);
+
+  return stringify(tokens, options, restoreEscapes, context.inputSourceMapTracker);
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/hex-name-shortener.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/hex-name-shortener.js
new file mode 100644
index 0000000..2af2f4e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/hex-name-shortener.js
@@ -0,0 +1,186 @@
+var HexNameShortener = {};
+
+var COLORS = {
+  aliceblue: '#f0f8ff',
+  antiquewhite: '#faebd7',
+  aqua: '#0ff',
+  aquamarine: '#7fffd4',
+  azure: '#f0ffff',
+  beige: '#f5f5dc',
+  bisque: '#ffe4c4',
+  black: '#000',
+  blanchedalmond: '#ffebcd',
+  blue: '#00f',
+  blueviolet: '#8a2be2',
+  brown: '#a52a2a',
+  burlywood: '#deb887',
+  cadetblue: '#5f9ea0',
+  chartreuse: '#7fff00',
+  chocolate: '#d2691e',
+  coral: '#ff7f50',
+  cornflowerblue: '#6495ed',
+  cornsilk: '#fff8dc',
+  crimson: '#dc143c',
+  cyan: '#0ff',
+  darkblue: '#00008b',
+  darkcyan: '#008b8b',
+  darkgoldenrod: '#b8860b',
+  darkgray: '#a9a9a9',
+  darkgreen: '#006400',
+  darkgrey: '#a9a9a9',
+  darkkhaki: '#bdb76b',
+  darkmagenta: '#8b008b',
+  darkolivegreen: '#556b2f',
+  darkorange: '#ff8c00',
+  darkorchid: '#9932cc',
+  darkred: '#8b0000',
+  darksalmon: '#e9967a',
+  darkseagreen: '#8fbc8f',
+  darkslateblue: '#483d8b',
+  darkslategray: '#2f4f4f',
+  darkslategrey: '#2f4f4f',
+  darkturquoise: '#00ced1',
+  darkviolet: '#9400d3',
+  deeppink: '#ff1493',
+  deepskyblue: '#00bfff',
+  dimgray: '#696969',
+  dimgrey: '#696969',
+  dodgerblue: '#1e90ff',
+  firebrick: '#b22222',
+  floralwhite: '#fffaf0',
+  forestgreen: '#228b22',
+  fuchsia: '#f0f',
+  gainsboro: '#dcdcdc',
+  ghostwhite: '#f8f8ff',
+  gold: '#ffd700',
+  goldenrod: '#daa520',
+  gray: '#808080',
+  green: '#008000',
+  greenyellow: '#adff2f',
+  grey: '#808080',
+  honeydew: '#f0fff0',
+  hotpink: '#ff69b4',
+  indianred: '#cd5c5c',
+  indigo: '#4b0082',
+  ivory: '#fffff0',
+  khaki: '#f0e68c',
+  lavender: '#e6e6fa',
+  lavenderblush: '#fff0f5',
+  lawngreen: '#7cfc00',
+  lemonchiffon: '#fffacd',
+  lightblue: '#add8e6',
+  lightcoral: '#f08080',
+  lightcyan: '#e0ffff',
+  lightgoldenrodyellow: '#fafad2',
+  lightgray: '#d3d3d3',
+  lightgreen: '#90ee90',
+  lightgrey: '#d3d3d3',
+  lightpink: '#ffb6c1',
+  lightsalmon: '#ffa07a',
+  lightseagreen: '#20b2aa',
+  lightskyblue: '#87cefa',
+  lightslategray: '#778899',
+  lightslategrey: '#778899',
+  lightsteelblue: '#b0c4de',
+  lightyellow: '#ffffe0',
+  lime: '#0f0',
+  limegreen: '#32cd32',
+  linen: '#faf0e6',
+  magenta: '#ff00ff',
+  maroon: '#800000',
+  mediumaquamarine: '#66cdaa',
+  mediumblue: '#0000cd',
+  mediumorchid: '#ba55d3',
+  mediumpurple: '#9370db',
+  mediumseagreen: '#3cb371',
+  mediumslateblue: '#7b68ee',
+  mediumspringgreen: '#00fa9a',
+  mediumturquoise: '#48d1cc',
+  mediumvioletred: '#c71585',
+  midnightblue: '#191970',
+  mintcream: '#f5fffa',
+  mistyrose: '#ffe4e1',
+  moccasin: '#ffe4b5',
+  navajowhite: '#ffdead',
+  navy: '#000080',
+  oldlace: '#fdf5e6',
+  olive: '#808000',
+  olivedrab: '#6b8e23',
+  orange: '#ffa500',
+  orangered: '#ff4500',
+  orchid: '#da70d6',
+  palegoldenrod: '#eee8aa',
+  palegreen: '#98fb98',
+  paleturquoise: '#afeeee',
+  palevioletred: '#db7093',
+  papayawhip: '#ffefd5',
+  peachpuff: '#ffdab9',
+  peru: '#cd853f',
+  pink: '#ffc0cb',
+  plum: '#dda0dd',
+  powderblue: '#b0e0e6',
+  purple: '#800080',
+  rebeccapurple: '#663399',
+  red: '#f00',
+  rosybrown: '#bc8f8f',
+  royalblue: '#4169e1',
+  saddlebrown: '#8b4513',
+  salmon: '#fa8072',
+  sandybrown: '#f4a460',
+  seagreen: '#2e8b57',
+  seashell: '#fff5ee',
+  sienna: '#a0522d',
+  silver: '#c0c0c0',
+  skyblue: '#87ceeb',
+  slateblue: '#6a5acd',
+  slategray: '#708090',
+  slategrey: '#708090',
+  snow: '#fffafa',
+  springgreen: '#00ff7f',
+  steelblue: '#4682b4',
+  tan: '#d2b48c',
+  teal: '#008080',
+  thistle: '#d8bfd8',
+  tomato: '#ff6347',
+  turquoise: '#40e0d0',
+  violet: '#ee82ee',
+  wheat: '#f5deb3',
+  white: '#fff',
+  whitesmoke: '#f5f5f5',
+  yellow: '#ff0',
+  yellowgreen: '#9acd32'
+};
+
+var toHex = {};
+var toName = {};
+
+for (var name in COLORS) {
+  var hex = COLORS[name];
+  if (name.length < hex.length)
+    toName[hex] = name;
+  else
+    toHex[name] = hex;
+}
+
+var toHexPattern = new RegExp('(^| |,|\\))(' + Object.keys(toHex).join('|') + ')( |,|\\)|$)', 'ig');
+var toNamePattern = new RegExp('(' + Object.keys(toName).join('|') + ')([^a-f0-9]|$)', 'ig');
+
+function hexConverter(match, prefix, colorValue, suffix) {
+  return prefix + toHex[colorValue.toLowerCase()] + suffix;
+}
+
+function nameConverter(match, colorValue, suffix) {
+  return toName[colorValue.toLowerCase()] + suffix;
+}
+
+HexNameShortener.shorten = function (value) {
+  var hasHex = value.indexOf('#') > -1;
+  var shortened = value.replace(toHexPattern, hexConverter);
+
+  if (shortened != value)
+    shortened = shortened.replace(toHexPattern, hexConverter);
+
+  return hasHex ? shortened.replace(toNamePattern, nameConverter) : shortened;
+};
+
+module.exports = HexNameShortener;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/hsl.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/hsl.js
new file mode 100644
index 0000000..5c76b6e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/hsl.js
@@ -0,0 +1,67 @@
+// HSL to RGB converter. Both methods adapted from:
+// http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+
+function HSLColor(hue, saturation, lightness) {
+  this.hue = hue;
+  this.saturation = saturation;
+  this.lightness = lightness;
+}
+
+function hslToRgb(h, s, l) {
+  var r, g, b;
+
+  // normalize hue orientation b/w 0 and 360 degrees
+  h = h % 360;
+  if (h < 0)
+    h += 360;
+  h = ~~h / 360;
+
+  if (s < 0)
+    s = 0;
+  else if (s > 100)
+    s = 100;
+  s = ~~s / 100;
+
+  if (l < 0)
+    l = 0;
+  else if (l > 100)
+    l = 100;
+  l = ~~l / 100;
+
+  if (s === 0) {
+    r = g = b = l; // achromatic
+  } else {
+    var q = l < 0.5 ?
+      l * (1 + s) :
+      l + s - l * s;
+    var p = 2 * l - q;
+    r = hueToRgb(p, q, h + 1/3);
+    g = hueToRgb(p, q, h);
+    b = hueToRgb(p, q, h - 1/3);
+  }
+
+  return [~~(r * 255), ~~(g * 255), ~~(b * 255)];
+}
+
+function hueToRgb(p, q, t) {
+  if (t < 0) t += 1;
+  if (t > 1) t -= 1;
+  if (t < 1/6) return p + (q - p) * 6 * t;
+  if (t < 1/2) return q;
+  if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+  return p;
+}
+
+HSLColor.prototype.toHex = function () {
+  var asRgb = hslToRgb(this.hue, this.saturation, this.lightness);
+  var redAsHex = asRgb[0].toString(16);
+  var greenAsHex = asRgb[1].toString(16);
+  var blueAsHex = asRgb[2].toString(16);
+
+  return '#' +
+    ((redAsHex.length == 1 ? '0' : '') + redAsHex) +
+    ((greenAsHex.length == 1 ? '0' : '') + greenAsHex) +
+    ((blueAsHex.length == 1 ? '0' : '') + blueAsHex);
+};
+
+module.exports = HSLColor;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/rgb.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/rgb.js
new file mode 100644
index 0000000..2f94548
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/colors/rgb.js
@@ -0,0 +1,16 @@
+function RGB(red, green, blue) {
+  this.red = red;
+  this.green = green;
+  this.blue = blue;
+}
+
+RGB.prototype.toHex = function () {
+  var red = Math.max(0, Math.min(~~this.red, 255));
+  var green = Math.max(0, Math.min(~~this.green, 255));
+  var blue = Math.max(0, Math.min(~~this.blue, 255));
+
+  // Credit: Asen  http://jsbin.com/UPUmaGOc/2/edit?js,console
+  return '#' + ('00000' + (red << 16 | green << 8 | blue).toString(16)).slice(-6);
+};
+
+module.exports = RGB;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/imports/inliner.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/imports/inliner.js
new file mode 100644
index 0000000..9a58651
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/imports/inliner.js
@@ -0,0 +1,393 @@
+var fs = require('fs');
+var path = require('path');
+var http = require('http');
+var https = require('https');
+var url = require('url');
+
+var rewriteUrls = require('../urls/rewrite');
+var split = require('../utils/split');
+var override = require('../utils/object.js').override;
+
+var MAP_MARKER = /\/\*# sourceMappingURL=(\S+) \*\//;
+var REMOTE_RESOURCE = /^(https?:)?\/\//;
+var NO_PROTOCOL_RESOURCE = /^\/\//;
+
+function ImportInliner (context) {
+  this.outerContext = context;
+}
+
+ImportInliner.prototype.process = function (data, context) {
+  var root = this.outerContext.options.root;
+
+  context = override(context, {
+    baseRelativeTo: this.outerContext.options.relativeTo || root,
+    debug: this.outerContext.options.debug,
+    done: [],
+    errors: this.outerContext.errors,
+    left: [],
+    inliner: this.outerContext.options.inliner,
+    rebase: this.outerContext.options.rebase,
+    relativeTo: this.outerContext.options.relativeTo || root,
+    root: root,
+    sourceReader: this.outerContext.sourceReader,
+    sourceTracker: this.outerContext.sourceTracker,
+    warnings: this.outerContext.warnings,
+    visited: []
+  });
+
+  return importFrom(data, context);
+};
+
+function importFrom(data, context) {
+  if (context.shallow) {
+    context.shallow = false;
+    context.done.push(data);
+    return processNext(context);
+  }
+
+  var nextStart = 0;
+  var nextEnd = 0;
+  var cursor = 0;
+  var isComment = commentScanner(data);
+
+  for (; nextEnd < data.length;) {
+    nextStart = nextImportAt(data, cursor);
+    if (nextStart == -1)
+      break;
+
+    if (isComment(nextStart)) {
+      cursor = nextStart + 1;
+      continue;
+    }
+
+    nextEnd = data.indexOf(';', nextStart);
+    if (nextEnd == -1) {
+      cursor = data.length;
+      data = '';
+      break;
+    }
+
+    var noImportPart = data.substring(0, nextStart);
+    context.done.push(noImportPart);
+    context.left.unshift([data.substring(nextEnd + 1), context]);
+    context.afterContent = hasContent(noImportPart);
+    return inline(data, nextStart, nextEnd, context);
+  }
+
+  // no @import matched in current data
+  context.done.push(data);
+  return processNext(context);
+}
+
+function rebaseMap(data, source) {
+  return data.replace(MAP_MARKER, function (match, sourceMapUrl) {
+    return REMOTE_RESOURCE.test(sourceMapUrl) ?
+      match :
+      match.replace(sourceMapUrl, url.resolve(source, sourceMapUrl));
+  });
+}
+
+function nextImportAt(data, cursor) {
+  var nextLowerCase = data.indexOf('@import', cursor);
+  var nextUpperCase = data.indexOf('@IMPORT', cursor);
+
+  if (nextLowerCase > -1 && nextUpperCase == -1)
+    return nextLowerCase;
+  else if (nextLowerCase == -1 && nextUpperCase > -1)
+    return nextUpperCase;
+  else
+    return Math.min(nextLowerCase, nextUpperCase);
+}
+
+function processNext(context) {
+  return context.left.length > 0 ?
+    importFrom.apply(null, context.left.shift()) :
+    context.whenDone(context.done.join(''));
+}
+
+function commentScanner(data) {
+  var commentRegex = /(\/\*(?!\*\/)[\s\S]*?\*\/)/;
+  var lastStartIndex = 0;
+  var lastEndIndex = 0;
+  var noComments = false;
+
+  // test whether an index is located within a comment
+  return function scanner(idx) {
+    var comment;
+    var localStartIndex = 0;
+    var localEndIndex = 0;
+    var globalStartIndex = 0;
+    var globalEndIndex = 0;
+
+    // return if we know there are no more comments
+    if (noComments)
+      return false;
+
+    // idx can be still within last matched comment (many @import statements inside one comment)
+    if (idx > lastStartIndex && idx < lastEndIndex)
+      return true;
+
+    comment = data.match(commentRegex);
+
+    if (!comment) {
+      noComments = true;
+      return false;
+    }
+
+    // get the indexes relative to the current data chunk
+    lastStartIndex = localStartIndex = comment.index;
+    localEndIndex = localStartIndex + comment[0].length;
+
+    // calculate the indexes relative to the full original data
+    globalEndIndex = localEndIndex + lastEndIndex;
+    globalStartIndex = globalEndIndex - comment[0].length;
+
+    // chop off data up to and including current comment block
+    data = data.substring(localEndIndex);
+    lastEndIndex = globalEndIndex;
+
+    // re-run scan if comment ended before the idx
+    if (globalEndIndex < idx)
+      return scanner(idx);
+
+    return globalEndIndex > idx && idx > globalStartIndex;
+  };
+}
+
+function hasContent(data) {
+  var isComment = commentScanner(data);
+  var firstContentIdx = -1;
+  while (true) {
+    firstContentIdx = data.indexOf('{', firstContentIdx + 1);
+    if (firstContentIdx == -1 || !isComment(firstContentIdx))
+      break;
+  }
+
+  return firstContentIdx > -1;
+}
+
+function inline(data, nextStart, nextEnd, context) {
+  context.shallow = data.indexOf('@shallow') > 0;
+
+  var importDeclaration = data
+    .substring(nextImportAt(data, nextStart) + '@import'.length + 1, nextEnd)
+    .replace(/@shallow\)$/, ')')
+    .trim();
+
+  var viaUrl = importDeclaration.indexOf('url(') === 0;
+  var urlStartsAt = viaUrl ? 4 : 0;
+  var isQuoted = /^['"]/.exec(importDeclaration.substring(urlStartsAt, urlStartsAt + 2));
+  var urlEndsAt = isQuoted ?
+    importDeclaration.indexOf(isQuoted[0], urlStartsAt + 1) :
+    split(importDeclaration, ' ')[0].length - (viaUrl ? 1 : 0);
+
+  var importedFile = importDeclaration
+    .substring(urlStartsAt, urlEndsAt)
+    .replace(/['"]/g, '')
+    .replace(/\)$/, '')
+    .trim();
+
+  var mediaQuery = importDeclaration
+    .substring(urlEndsAt + 1)
+    .replace(/^\)/, '')
+    .trim();
+
+  var isRemote = context.isRemote || REMOTE_RESOURCE.test(importedFile);
+
+  if (isRemote && (context.localOnly || !allowedResource(importedFile, true, context.imports))) {
+    if (context.afterContent || hasContent(context.done.join('')))
+      context.warnings.push('Ignoring remote @import of "' + importedFile + '" as no callback given.');
+    else
+      restoreImport(importedFile, mediaQuery, context);
+
+    return processNext(context);
+  }
+
+  if (!isRemote && !allowedResource(importedFile, false, context.imports)) {
+    if (context.afterImport)
+      context.warnings.push('Ignoring local @import of "' + importedFile + '" as after other inlined content.');
+    else
+      restoreImport(importedFile, mediaQuery, context);
+    return processNext(context);
+  }
+
+  if (!isRemote && context.afterContent) {
+    context.warnings.push('Ignoring local @import of "' + importedFile + '" as after other CSS content.');
+    return processNext(context);
+  }
+
+  var method = isRemote ? inlineRemoteResource : inlineLocalResource;
+  return method(importedFile, mediaQuery, context);
+}
+
+function allowedResource(importedFile, isRemote, rules) {
+  if (rules.length === 0)
+    return false;
+
+  if (isRemote && NO_PROTOCOL_RESOURCE.test(importedFile))
+    importedFile = 'http:' + importedFile;
+
+  var match = isRemote ?
+    url.parse(importedFile).host :
+    importedFile;
+  var allowed = true;
+
+  for (var i = 0; i < rules.length; i++) {
+    var rule = rules[i];
+
+    if (rule == 'all')
+      allowed = true;
+    else if (isRemote && rule == 'local')
+      allowed = false;
+    else if (isRemote && rule == 'remote')
+      allowed = true;
+    else if (!isRemote && rule == 'remote')
+      allowed = false;
+    else if (!isRemote && rule == 'local')
+      allowed = true;
+    else if (rule[0] == '!' && rule.substring(1) === match)
+      allowed = false;
+  }
+
+  return allowed;
+}
+
+function inlineRemoteResource(importedFile, mediaQuery, context) {
+  var importedUrl = REMOTE_RESOURCE.test(importedFile) ?
+    importedFile :
+    url.resolve(context.relativeTo, importedFile);
+  var originalUrl = importedUrl;
+
+  if (NO_PROTOCOL_RESOURCE.test(importedUrl))
+    importedUrl = 'http:' + importedUrl;
+
+  if (context.visited.indexOf(importedUrl) > -1)
+    return processNext(context);
+
+
+  if (context.debug)
+    console.error('Inlining remote stylesheet: ' + importedUrl);
+
+  context.visited.push(importedUrl);
+
+  var get = importedUrl.indexOf('http://') === 0 ?
+    http.get :
+    https.get;
+
+  var errorHandled = false;
+  function handleError(message) {
+    if (errorHandled)
+      return;
+
+    errorHandled = true;
+    context.errors.push('Broken @import declaration of "' + importedUrl + '" - ' + message);
+    restoreImport(importedUrl, mediaQuery, context);
+
+    process.nextTick(function () {
+      processNext(context);
+    });
+  }
+
+  var requestOptions = override(url.parse(importedUrl), context.inliner.request);
+  if (context.inliner.request.hostname !== undefined)
+    requestOptions.path = requestOptions.href;
+
+  get(requestOptions, function (res) {
+    if (res.statusCode < 200 || res.statusCode > 399) {
+      return handleError('error ' + res.statusCode);
+    } else if (res.statusCode > 299) {
+      var movedUrl = url.resolve(importedUrl, res.headers.location);
+      return inlineRemoteResource(movedUrl, mediaQuery, context);
+    }
+
+    var chunks = [];
+    var parsedUrl = url.parse(importedUrl);
+    res.on('data', function (chunk) {
+      chunks.push(chunk.toString());
+    });
+    res.on('end', function () {
+      var importedData = chunks.join('');
+      if (context.rebase)
+        importedData = rewriteUrls(importedData, { toBase: originalUrl }, context);
+      context.sourceReader.trackSource(importedUrl, importedData);
+      importedData = context.sourceTracker.store(importedUrl, importedData);
+      importedData = rebaseMap(importedData, importedUrl);
+
+      if (mediaQuery.length > 0)
+        importedData = '@media ' + mediaQuery + '{' + importedData + '}';
+
+      context.afterImport = true;
+
+      var newContext = override(context, {
+        isRemote: true,
+        relativeTo: parsedUrl.protocol + '//' + parsedUrl.host + parsedUrl.pathname
+      });
+
+      process.nextTick(function () {
+        importFrom(importedData, newContext);
+      });
+    });
+  })
+  .on('error', function (res) {
+    handleError(res.message);
+  })
+  .on('timeout', function () {
+    handleError('timeout');
+  })
+  .setTimeout(context.inliner.timeout);
+}
+
+function inlineLocalResource(importedFile, mediaQuery, context) {
+  var relativeTo = importedFile[0] == '/' ?
+    context.root :
+    context.relativeTo;
+
+  var fullPath = path.resolve(path.join(relativeTo, importedFile));
+
+  if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
+    context.errors.push('Broken @import declaration of "' + importedFile + '"');
+    return processNext(context);
+  }
+
+  if (context.visited.indexOf(fullPath) > -1)
+    return processNext(context);
+
+
+  if (context.debug)
+    console.error('Inlining local stylesheet: ' + fullPath);
+
+  context.visited.push(fullPath);
+
+  var importRelativeTo = path.dirname(fullPath);
+  var importedData = fs.readFileSync(fullPath, 'utf8');
+  if (context.rebase) {
+    var rewriteOptions = {
+      relative: true,
+      fromBase: importRelativeTo,
+      toBase: context.baseRelativeTo
+    };
+    importedData = rewriteUrls(importedData, rewriteOptions, context);
+  }
+
+  var relativePath = path.relative(context.root, fullPath);
+  context.sourceReader.trackSource(relativePath, importedData);
+  importedData = context.sourceTracker.store(relativePath, importedData);
+
+  if (mediaQuery.length > 0)
+    importedData = '@media ' + mediaQuery + '{' + importedData + '}';
+
+  context.afterImport = true;
+
+  var newContext = override(context, {
+    relativeTo: importRelativeTo
+  });
+
+  return importFrom(importedData, newContext);
+}
+
+function restoreImport(importedUrl, mediaQuery, context) {
+  var restoredImport = '@import url(' + importedUrl + ')' + (mediaQuery.length > 0 ? ' ' + mediaQuery : '') + ';';
+  context.done.push(restoredImport);
+}
+
+module.exports = ImportInliner;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/break-up.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/break-up.js
new file mode 100644
index 0000000..ae9701d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/break-up.js
@@ -0,0 +1,329 @@
+var wrapSingle = require('./wrap-for-optimizing').single;
+
+var split = require('../utils/split');
+var MULTIPLEX_SEPARATOR = ',';
+
+function _colorFilter(validator) {
+  return function (value) {
+    return value[0] == 'invert' || validator.isValidColor(value[0]);
+  };
+}
+
+function _styleFilter(validator) {
+  return function (value) {
+    return value[0] != 'inherit' && validator.isValidStyle(value[0]);
+  };
+}
+
+function _wrapDefault(name, property, compactable) {
+  var descriptor = compactable[name];
+  if (descriptor.doubleValues && descriptor.defaultValue.length == 2)
+    return wrapSingle([[name, property.important], [descriptor.defaultValue[0]], [descriptor.defaultValue[1]]]);
+  else if (descriptor.doubleValues && descriptor.defaultValue.length == 1)
+    return wrapSingle([[name, property.important], [descriptor.defaultValue[0]]]);
+  else
+    return wrapSingle([[name, property.important], [descriptor.defaultValue]]);
+}
+
+function _widthFilter(validator) {
+  return function (value) {
+    return value[0] != 'inherit' && validator.isValidWidth(value[0]);
+  };
+}
+
+function background(property, compactable, validator) {
+  var image = _wrapDefault('background-image', property, compactable);
+  var position = _wrapDefault('background-position', property, compactable);
+  var size = _wrapDefault('background-size', property, compactable);
+  var repeat = _wrapDefault('background-repeat', property, compactable);
+  var attachment = _wrapDefault('background-attachment', property, compactable);
+  var origin = _wrapDefault('background-origin', property, compactable);
+  var clip = _wrapDefault('background-clip', property, compactable);
+  var color = _wrapDefault('background-color', property, compactable);
+  var components = [image, position, size, repeat, attachment, origin, clip, color];
+  var values = property.value;
+
+  var positionSet = false;
+  var clipSet = false;
+  var originSet = false;
+  var repeatSet = false;
+
+  if (property.value.length == 1 && property.value[0][0] == 'inherit') {
+    // NOTE: 'inherit' is not a valid value for background-attachment
+    color.value = image.value =  repeat.value = position.value = size.value = origin.value = clip.value = property.value;
+    return components;
+  }
+
+  for (var i = values.length - 1; i >= 0; i--) {
+    var value = values[i];
+
+    if (validator.isValidBackgroundAttachment(value[0])) {
+      attachment.value = [value];
+    } else if (validator.isValidBackgroundBox(value[0])) {
+      if (clipSet) {
+        origin.value = [value];
+        originSet = true;
+      } else {
+        clip.value = [value];
+        clipSet = true;
+      }
+    } else if (validator.isValidBackgroundRepeat(value[0])) {
+      if (repeatSet) {
+        repeat.value.unshift(value);
+      } else {
+        repeat.value = [value];
+        repeatSet = true;
+      }
+    } else if (validator.isValidBackgroundPositionPart(value[0]) || validator.isValidBackgroundSizePart(value[0])) {
+      if (i > 0) {
+        var previousValue = values[i - 1];
+
+        if (previousValue[0].indexOf('/') > 0) {
+          var twoParts = split(previousValue[0], '/');
+          // NOTE: we do this slicing as value may contain metadata too, like for source maps
+          size.value = [[twoParts.pop()].concat(previousValue.slice(1)), value];
+          values[i - 1] = [twoParts.pop()].concat(previousValue.slice(1));
+        } else if (i > 1 && values[i - 2] == '/') {
+          size.value = [previousValue, value];
+          i -= 2;
+        } else if (previousValue[0] == '/') {
+          size.value = [value];
+        } else {
+          if (!positionSet)
+            position.value = [];
+
+          position.value.unshift(value);
+          positionSet = true;
+        }
+      } else {
+        if (!positionSet)
+          position.value = [];
+
+        position.value.unshift(value);
+        positionSet = true;
+      }
+    } else if (validator.isValidBackgroundPositionAndSize(value[0])) {
+      var sizeValue = split(value[0], '/');
+      // NOTE: we do this slicing as value may contain metadata too, like for source maps
+      size.value = [[sizeValue.pop()].concat(value.slice(1))];
+      position.value = [[sizeValue.pop()].concat(value.slice(1))];
+    } else if ((color.value[0][0] == compactable[color.name].defaultValue || color.value[0][0] == 'none') && validator.isValidColor(value[0])) {
+      color.value = [value];
+    } else if (validator.isValidUrl(value[0]) || validator.isValidFunction(value[0])) {
+      image.value = [value];
+    }
+  }
+
+  if (clipSet && !originSet)
+    origin.value = clip.value.slice(0);
+
+  return components;
+}
+
+function borderRadius(property, compactable) {
+  var values = property.value;
+  var splitAt = -1;
+
+  for (var i = 0, l = values.length; i < l; i++) {
+    if (values[i][0] == '/') {
+      splitAt = i;
+      break;
+    }
+  }
+
+  if (splitAt == -1)
+    return fourValues(property, compactable);
+
+  var target = _wrapDefault(property.name, property, compactable);
+  target.value = values.slice(0, splitAt);
+  target.components = fourValues(target, compactable);
+
+  var remainder = _wrapDefault(property.name, property, compactable);
+  remainder.value = values.slice(splitAt + 1);
+  remainder.components = fourValues(remainder, compactable);
+
+  for (var j = 0; j < 4; j++) {
+    target.components[j].multiplex = true;
+    target.components[j].value = target.components[j].value.concat([['/']]).concat(remainder.components[j].value);
+  }
+
+  return target.components;
+}
+
+function fourValues(property, compactable) {
+  var componentNames = compactable[property.name].components;
+  var components = [];
+  var value = property.value;
+
+  if (value.length < 1)
+    return [];
+
+  if (value.length < 2)
+    value[1] = value[0];
+  if (value.length < 3)
+    value[2] = value[0];
+  if (value.length < 4)
+    value[3] = value[1];
+
+  for (var i = componentNames.length - 1; i >= 0; i--) {
+    var component = wrapSingle([[componentNames[i], property.important]]);
+    component.value = [value[i]];
+    components.unshift(component);
+  }
+
+  return components;
+}
+
+function multiplex(splitWith) {
+  return function (property, compactable, validator) {
+    var splitsAt = [];
+    var values = property.value;
+    var i, j, l, m;
+
+    // find split commas
+    for (i = 0, l = values.length; i < l; i++) {
+      if (values[i][0] == ',')
+        splitsAt.push(i);
+    }
+
+    if (splitsAt.length === 0)
+      return splitWith(property, compactable, validator);
+
+    var splitComponents = [];
+
+    // split over commas, and into components
+    for (i = 0, l = splitsAt.length; i <= l; i++) {
+      var from = i === 0 ? 0 : splitsAt[i - 1] + 1;
+      var to = i < l ? splitsAt[i] : values.length;
+
+      var _property = _wrapDefault(property.name, property, compactable);
+      _property.value = values.slice(from, to);
+
+      splitComponents.push(splitWith(_property, compactable, validator));
+    }
+
+    var components = splitComponents[0];
+
+    // group component values from each split
+    for (i = 0, l = components.length; i < l; i++) {
+      components[i].multiplex = true;
+
+      for (j = 1, m = splitComponents.length; j < m; j++) {
+        components[i].value.push([MULTIPLEX_SEPARATOR]);
+        Array.prototype.push.apply(components[i].value, splitComponents[j][i].value);
+      }
+    }
+
+    return components;
+  };
+}
+
+function listStyle(property, compactable, validator) {
+  var type = _wrapDefault('list-style-type', property, compactable);
+  var position = _wrapDefault('list-style-position', property, compactable);
+  var image = _wrapDefault('list-style-image', property, compactable);
+  var components = [type, position, image];
+
+  if (property.value.length == 1 && property.value[0][0] == 'inherit') {
+    type.value = position.value = image.value = [property.value[0]];
+    return components;
+  }
+
+  var values = property.value.slice(0);
+  var total = values.length;
+  var index = 0;
+
+  // `image` first...
+  for (index = 0, total = values.length; index < total; index++) {
+    if (validator.isValidUrl(values[index][0]) || values[index][0] == '0') {
+      image.value = [values[index]];
+      values.splice(index, 1);
+      break;
+    }
+  }
+
+  // ... then `type`...
+  for (index = 0, total = values.length; index < total; index++) {
+    if (validator.isValidListStyleType(values[index][0])) {
+      type.value = [values[index]];
+      values.splice(index, 1);
+      break;
+    }
+  }
+
+  // ... and what's left is a `position`
+  if (values.length > 0 && validator.isValidListStylePosition(values[0][0]))
+    position.value = [values[0]];
+
+  return components;
+}
+
+function widthStyleColor(property, compactable, validator) {
+  var descriptor = compactable[property.name];
+  var components = [
+    _wrapDefault(descriptor.components[0], property, compactable),
+    _wrapDefault(descriptor.components[1], property, compactable),
+    _wrapDefault(descriptor.components[2], property, compactable)
+  ];
+  var color, style, width;
+
+  for (var i = 0; i < 3; i++) {
+    var component = components[i];
+
+    if (component.name.indexOf('color') > 0)
+      color = component;
+    else if (component.name.indexOf('style') > 0)
+      style = component;
+    else
+      width = component;
+  }
+
+  if ((property.value.length == 1 && property.value[0][0] == 'inherit') ||
+      (property.value.length == 3 && property.value[0][0] == 'inherit' && property.value[1][0] == 'inherit' && property.value[2][0] == 'inherit')) {
+    color.value = style.value = width.value = [property.value[0]];
+    return components;
+  }
+
+  var values = property.value.slice(0);
+  var match, matches;
+
+  // NOTE: usually users don't follow the required order of parts in this shorthand,
+  // so we'll try to parse it caring as little about order as possible
+
+  if (values.length > 0) {
+    matches = values.filter(_widthFilter(validator));
+    match = matches.length > 1 && (matches[0][0] == 'none' || matches[0][0] == 'auto') ? matches[1] : matches[0];
+    if (match) {
+      width.value = [match];
+      values.splice(values.indexOf(match), 1);
+    }
+  }
+
+  if (values.length > 0) {
+    match = values.filter(_styleFilter(validator))[0];
+    if (match) {
+      style.value = [match];
+      values.splice(values.indexOf(match), 1);
+    }
+  }
+
+  if (values.length > 0) {
+    match = values.filter(_colorFilter(validator))[0];
+    if (match) {
+      color.value = [match];
+      values.splice(values.indexOf(match), 1);
+    }
+  }
+
+  return components;
+}
+
+module.exports = {
+  background: background,
+  border: widthStyleColor,
+  borderRadius: borderRadius,
+  fourValues: fourValues,
+  listStyle: listStyle,
+  multiplex: multiplex,
+  outline: widthStyleColor
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/can-override.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/can-override.js
new file mode 100644
index 0000000..88857d7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/can-override.js
@@ -0,0 +1,120 @@
+// Functions that decide what value can override what.
+// The main purpose is to disallow removing CSS fallbacks.
+// A separate implementation is needed for every different kind of CSS property.
+// -----
+// The generic idea is that properties that have wider browser support are 'more understandable'
+// than others and that 'less understandable' values can't override more understandable ones.
+
+// Use when two tokens of the same property can always be merged
+function always() {
+  return true;
+}
+
+function backgroundImage(property1, property2, validator) {
+  // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
+  // Understandability: (none | url | inherit) > (same function) > (same value)
+
+  // (none | url)
+  var image1 = property1.value[0][0];
+  var image2 = property2.value[0][0];
+
+  if (image2 == 'none' || image2 == 'inherit' || validator.isValidUrl(image2))
+    return true;
+  if (image1 == 'none' || image1 == 'inherit' || validator.isValidUrl(image1))
+    return false;
+
+  // Functions with the same name can override each other; same values can override each other
+  return sameFunctionOrValue(property1, property2, validator);
+}
+
+function border(property1, property2, validator) {
+  return color(property1.components[2], property2.components[2], validator);
+}
+
+// Use for color properties (color, background-color, border-color, etc.)
+function color(property1, property2, validator) {
+  // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
+  // Understandability: (hex | named) > (rgba | hsla) > (same function name) > anything else
+  // NOTE: at this point rgb and hsl are replaced by hex values by clean-css
+
+  var color1 = property1.value[0][0];
+  var color2 = property2.value[0][0];
+
+  // (hex | named)
+  if (validator.isValidNamedColor(color2) || validator.isValidHexColor(color2))
+    return true;
+  if (validator.isValidNamedColor(color1) || validator.isValidHexColor(color1))
+    return false;
+
+  // (rgba|hsla)
+  if (validator.isValidRgbaColor(color2) || validator.isValidHslaColor(color2))
+    return true;
+  if (validator.isValidRgbaColor(color1) || validator.isValidHslaColor(color1))
+    return false;
+
+  // Functions with the same name can override each other; same values can override each other
+  return sameFunctionOrValue(property1, property2, validator);
+}
+
+function twoOptionalFunctions(property1, property2, validator) {
+  var value1 = property1.value[0][0];
+  var value2 = property2.value[0][0];
+
+  return !(validator.isValidFunction(value1) ^ validator.isValidFunction(value2));
+}
+
+function sameValue(property1, property2) {
+  var value1 = property1.value[0][0];
+  var value2 = property2.value[0][0];
+
+  return value1 === value2;
+}
+
+function sameFunctionOrValue(property1, property2, validator) {
+  var value1 = property1.value[0][0];
+  var value2 = property2.value[0][0];
+
+  // Functions with the same name can override each other
+  if (validator.areSameFunction(value1, value2))
+    return true;
+
+  return value1 === value2;
+}
+
+// Use for properties containing CSS units (margin-top, padding-left, etc.)
+function unit(property1, property2, validator) {
+  // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
+  // Understandability: (unit without functions) > (same functions | standard functions) > anything else
+  // NOTE: there is no point in having different vendor-specific functions override each other or standard functions,
+  //       or having standard functions override vendor-specific functions, but standard functions can override each other
+  // NOTE: vendor-specific property values are not taken into consideration here at the moment
+  var value1 = property1.value[0][0];
+  var value2 = property2.value[0][0];
+
+  if (validator.isValidAndCompatibleUnitWithoutFunction(value1) && !validator.isValidAndCompatibleUnitWithoutFunction(value2))
+    return false;
+
+  if (validator.isValidUnitWithoutFunction(value2))
+    return true;
+  if (validator.isValidUnitWithoutFunction(value1))
+    return false;
+
+  // Standard non-vendor-prefixed functions can override each other
+  if (validator.isValidFunctionWithoutVendorPrefix(value2) && validator.isValidFunctionWithoutVendorPrefix(value1)) {
+    return true;
+  }
+
+  // Functions with the same name can override each other; same values can override each other
+  return sameFunctionOrValue(property1, property2, validator);
+}
+
+module.exports = {
+  always: always,
+  backgroundImage: backgroundImage,
+  border: border,
+  color: color,
+  sameValue: sameValue,
+  sameFunctionOrValue: sameFunctionOrValue,
+  twoOptionalFunctions: twoOptionalFunctions,
+  unit: unit
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/clone.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/clone.js
new file mode 100644
index 0000000..5be6441
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/clone.js
@@ -0,0 +1,26 @@
+var wrapSingle = require('./wrap-for-optimizing').single;
+
+function deep(property) {
+  var cloned = shallow(property);
+  for (var i = property.components.length - 1; i >= 0; i--) {
+    var component = shallow(property.components[i]);
+    component.value = property.components[i].value.slice(0);
+    cloned.components.unshift(component);
+  }
+
+  cloned.dirty = true;
+  cloned.value = property.value.slice(0);
+
+  return cloned;
+}
+
+function shallow(property) {
+  var cloned = wrapSingle([[property.name, property.important, property.hack]]);
+  cloned.unused = false;
+  return cloned;
+}
+
+module.exports = {
+  deep: deep,
+  shallow: shallow
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/compactable.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/compactable.js
new file mode 100644
index 0000000..dce0238
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/compactable.js
@@ -0,0 +1,285 @@
+// Contains the interpretation of CSS properties, as used by the property optimizer
+
+var breakUp = require('./break-up');
+var canOverride = require('./can-override');
+var restore = require('./restore');
+
+// Properties to process
+// Extend this object in order to add support for more properties in the optimizer.
+//
+// Each key in this object represents a CSS property and should be an object.
+// Such an object contains properties that describe how the represented CSS property should be handled.
+// Possible options:
+//
+// * components: array (Only specify for shorthand properties.)
+//   Contains the names of the granular properties this shorthand compacts.
+//
+// * canOverride: function (Default is canOverride.sameValue - meaning that they'll only be merged if they have the same value.)
+//   Returns whether two tokens of this property can be merged with each other.
+//   This property has no meaning for shorthands.
+//
+// * defaultValue: string
+//   Specifies the default value of the property according to the CSS standard.
+//   For shorthand, this is used when every component is set to its default value, therefore it should be the shortest possible default value of all the components.
+//
+// * shortestValue: string
+//   Specifies the shortest possible value the property can possibly have.
+//   (Falls back to defaultValue if unspecified.)
+//
+// * breakUp: function (Only specify for shorthand properties.)
+//   Breaks the shorthand up to its components.
+//
+// * restore: function (Only specify for shorthand properties.)
+//   Puts the shorthand together from its components.
+//
+var compactable = {
+  'color': {
+    canOverride: canOverride.color,
+    defaultValue: 'transparent',
+    shortestValue: 'red'
+  },
+  'background': {
+    components: [
+      'background-image',
+      'background-position',
+      'background-size',
+      'background-repeat',
+      'background-attachment',
+      'background-origin',
+      'background-clip',
+      'background-color'
+    ],
+    breakUp: breakUp.multiplex(breakUp.background),
+    defaultValue: '0 0',
+    restore: restore.multiplex(restore.background),
+    shortestValue: '0',
+    shorthand: true
+  },
+  'background-clip': {
+    canOverride: canOverride.always,
+    defaultValue: 'border-box',
+    shortestValue: 'border-box'
+  },
+  'background-color': {
+    canOverride: canOverride.color,
+    defaultValue: 'transparent',
+    multiplexLastOnly: true,
+    nonMergeableValue: 'none',
+    shortestValue: 'red'
+  },
+  'background-image': {
+    canOverride: canOverride.backgroundImage,
+    defaultValue: 'none'
+  },
+  'background-origin': {
+    canOverride: canOverride.always,
+    defaultValue: 'padding-box',
+    shortestValue: 'border-box'
+  },
+  'background-repeat': {
+    canOverride: canOverride.always,
+    defaultValue: ['repeat'],
+    doubleValues: true
+  },
+  'background-position': {
+    canOverride: canOverride.always,
+    defaultValue: ['0', '0'],
+    doubleValues: true,
+    shortestValue: '0'
+  },
+  'background-size': {
+    canOverride: canOverride.always,
+    defaultValue: ['auto'],
+    doubleValues: true,
+    shortestValue: '0 0'
+  },
+  'background-attachment': {
+    canOverride: canOverride.always,
+    defaultValue: 'scroll'
+  },
+  'border': {
+    breakUp: breakUp.border,
+    canOverride: canOverride.border,
+    components: [
+      'border-width',
+      'border-style',
+      'border-color'
+    ],
+    defaultValue: 'none',
+    restore: restore.withoutDefaults,
+    shorthand: true
+  },
+  'border-color': {
+    canOverride: canOverride.color,
+    defaultValue: 'none',
+    shorthand: true
+  },
+  'border-style': {
+    canOverride: canOverride.always,
+    defaultValue: 'none',
+    shorthand: true
+  },
+  'border-width': {
+    canOverride: canOverride.unit,
+    defaultValue: 'medium',
+    shortestValue: '0',
+    shorthand: true
+  },
+  'list-style': {
+    components: [
+      'list-style-type',
+      'list-style-position',
+      'list-style-image'
+    ],
+    canOverride: canOverride.always,
+    breakUp: breakUp.listStyle,
+    restore: restore.withoutDefaults,
+    defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol>
+    shortestValue: 'none',
+    shorthand: true
+  },
+  'list-style-type' : {
+    canOverride: canOverride.always,
+    defaultValue: '__hack',
+    // NOTE: we can't tell the real default value here, it's 'disc' for <ul> and 'decimal' for <ol>
+    //       -- this is a hack, but it doesn't matter because this value will be either overridden or it will disappear at the final step anyway
+    shortestValue: 'none'
+  },
+  'list-style-position' : {
+    canOverride: canOverride.always,
+    defaultValue: 'outside',
+    shortestValue: 'inside'
+  },
+  'list-style-image' : {
+    canOverride: canOverride.always,
+    defaultValue: 'none'
+  },
+  'outline': {
+    components: [
+      'outline-color',
+      'outline-style',
+      'outline-width'
+    ],
+    breakUp: breakUp.outline,
+    restore: restore.withoutDefaults,
+    defaultValue: '0',
+    shorthand: true
+  },
+  'outline-color': {
+    canOverride: canOverride.color,
+    defaultValue: 'invert',
+    shortestValue: 'red'
+  },
+  'outline-style': {
+    canOverride: canOverride.always,
+    defaultValue: 'none'
+  },
+  'outline-width': {
+    canOverride: canOverride.unit,
+    defaultValue: 'medium',
+    shortestValue: '0'
+  },
+  '-moz-transform': {
+    canOverride: canOverride.sameFunctionOrValue
+  },
+  '-ms-transform': {
+    canOverride: canOverride.sameFunctionOrValue
+  },
+  '-webkit-transform': {
+    canOverride: canOverride.sameFunctionOrValue
+  },
+  'transform': {
+    canOverride: canOverride.sameFunctionOrValue
+  }
+};
+
+var addFourValueShorthand = function (prop, components, options) {
+  options = options || {};
+  compactable[prop] = {
+    canOverride: options.canOverride,
+    components: components,
+    breakUp: options.breakUp || breakUp.fourValues,
+    defaultValue: options.defaultValue || '0',
+    restore: options.restore || restore.fourValues,
+    shortestValue: options.shortestValue,
+    shorthand: true
+  };
+  for (var i = 0; i < components.length; i++) {
+    compactable[components[i]] = {
+      breakUp: options.breakUp || breakUp.fourValues,
+      canOverride: options.canOverride || canOverride.unit,
+      defaultValue: options.defaultValue || '0',
+      shortestValue: options.shortestValue
+    };
+  }
+};
+
+['', '-moz-', '-o-', '-webkit-'].forEach(function (prefix) {
+  addFourValueShorthand(prefix + 'border-radius', [
+    prefix + 'border-top-left-radius',
+    prefix + 'border-top-right-radius',
+    prefix + 'border-bottom-right-radius',
+    prefix + 'border-bottom-left-radius'
+  ], {
+    breakUp: breakUp.borderRadius,
+    restore: restore.borderRadius
+  });
+});
+
+addFourValueShorthand('border-color', [
+  'border-top-color',
+  'border-right-color',
+  'border-bottom-color',
+  'border-left-color'
+], {
+  breakUp: breakUp.fourValues,
+  canOverride: canOverride.color,
+  defaultValue: 'none',
+  shortestValue: 'red'
+});
+
+addFourValueShorthand('border-style', [
+  'border-top-style',
+  'border-right-style',
+  'border-bottom-style',
+  'border-left-style'
+], {
+  breakUp: breakUp.fourValues,
+  canOverride: canOverride.always,
+  defaultValue: 'none'
+});
+
+addFourValueShorthand('border-width', [
+  'border-top-width',
+  'border-right-width',
+  'border-bottom-width',
+  'border-left-width'
+], {
+  defaultValue: 'medium',
+  shortestValue: '0'
+});
+
+addFourValueShorthand('padding', [
+  'padding-top',
+  'padding-right',
+  'padding-bottom',
+  'padding-left'
+]);
+
+addFourValueShorthand('margin', [
+  'margin-top',
+  'margin-right',
+  'margin-bottom',
+  'margin-left'
+]);
+
+// Adds `componentOf` field to all longhands
+for (var property in compactable) {
+  if (compactable[property].shorthand) {
+    for (var i = 0, l = compactable[property].components.length; i < l; i++) {
+      compactable[compactable[property].components[i]].componentOf = property;
+    }
+  }
+}
+
+module.exports = compactable;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/every-combination.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/every-combination.js
new file mode 100644
index 0000000..f608ed6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/every-combination.js
@@ -0,0 +1,24 @@
+var shallowClone = require('./clone').shallow;
+
+var MULTIPLEX_SEPARATOR = ',';
+
+function everyCombination(fn, left, right, validator) {
+  var _left = shallowClone(left);
+  var _right = shallowClone(right);
+
+  for (var i = 0, l = left.value.length; i < l; i++) {
+    for (var j = 0, m = right.value.length; j < m; j++) {
+      if (left.value[i][0] == MULTIPLEX_SEPARATOR || right.value[j][0] == MULTIPLEX_SEPARATOR)
+        continue;
+
+      _left.value = [left.value[i]];
+      _right.value = [right.value[j]];
+      if (!fn(_left, _right, validator))
+        return false;
+    }
+  }
+
+  return true;
+}
+
+module.exports = everyCombination;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/has-inherit.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/has-inherit.js
new file mode 100644
index 0000000..96a103b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/has-inherit.js
@@ -0,0 +1,10 @@
+function hasInherit(property) {
+  for (var i = property.value.length - 1; i >= 0; i--) {
+    if (property.value[i][0] == 'inherit')
+      return true;
+  }
+
+  return false;
+}
+
+module.exports = hasInherit;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/optimizer.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/optimizer.js
new file mode 100644
index 0000000..702a63d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/optimizer.js
@@ -0,0 +1,206 @@
+var compactable = require('./compactable');
+var wrapForOptimizing = require('./wrap-for-optimizing').all;
+var populateComponents = require('./populate-components');
+var compactOverrides = require('./override-compactor');
+var compactShorthands = require('./shorthand-compactor');
+var removeUnused = require('./remove-unused');
+var restoreFromOptimizing = require('./restore-from-optimizing');
+var stringifyProperty = require('../stringifier/one-time').property;
+
+var shorthands = {
+  'animation-delay': ['animation'],
+  'animation-direction': ['animation'],
+  'animation-duration': ['animation'],
+  'animation-fill-mode': ['animation'],
+  'animation-iteration-count': ['animation'],
+  'animation-name': ['animation'],
+  'animation-play-state': ['animation'],
+  'animation-timing-function': ['animation'],
+  '-moz-animation-delay': ['-moz-animation'],
+  '-moz-animation-direction': ['-moz-animation'],
+  '-moz-animation-duration': ['-moz-animation'],
+  '-moz-animation-fill-mode': ['-moz-animation'],
+  '-moz-animation-iteration-count': ['-moz-animation'],
+  '-moz-animation-name': ['-moz-animation'],
+  '-moz-animation-play-state': ['-moz-animation'],
+  '-moz-animation-timing-function': ['-moz-animation'],
+  '-o-animation-delay': ['-o-animation'],
+  '-o-animation-direction': ['-o-animation'],
+  '-o-animation-duration': ['-o-animation'],
+  '-o-animation-fill-mode': ['-o-animation'],
+  '-o-animation-iteration-count': ['-o-animation'],
+  '-o-animation-name': ['-o-animation'],
+  '-o-animation-play-state': ['-o-animation'],
+  '-o-animation-timing-function': ['-o-animation'],
+  '-webkit-animation-delay': ['-webkit-animation'],
+  '-webkit-animation-direction': ['-webkit-animation'],
+  '-webkit-animation-duration': ['-webkit-animation'],
+  '-webkit-animation-fill-mode': ['-webkit-animation'],
+  '-webkit-animation-iteration-count': ['-webkit-animation'],
+  '-webkit-animation-name': ['-webkit-animation'],
+  '-webkit-animation-play-state': ['-webkit-animation'],
+  '-webkit-animation-timing-function': ['-webkit-animation'],
+  'border-color': ['border'],
+  'border-style': ['border'],
+  'border-width': ['border'],
+  'border-bottom': ['border'],
+  'border-bottom-color': ['border-bottom', 'border-color', 'border'],
+  'border-bottom-style': ['border-bottom', 'border-style', 'border'],
+  'border-bottom-width': ['border-bottom', 'border-width', 'border'],
+  'border-left': ['border'],
+  'border-left-color': ['border-left', 'border-color', 'border'],
+  'border-left-style': ['border-left', 'border-style', 'border'],
+  'border-left-width': ['border-left', 'border-width', 'border'],
+  'border-right': ['border'],
+  'border-right-color': ['border-right', 'border-color', 'border'],
+  'border-right-style': ['border-right', 'border-style', 'border'],
+  'border-right-width': ['border-right', 'border-width', 'border'],
+  'border-top': ['border'],
+  'border-top-color': ['border-top', 'border-color', 'border'],
+  'border-top-style': ['border-top', 'border-style', 'border'],
+  'border-top-width': ['border-top', 'border-width', 'border'],
+  'font-family': ['font'],
+  'font-size': ['font'],
+  'font-style': ['font'],
+  'font-variant': ['font'],
+  'font-weight': ['font'],
+  'transition-delay': ['transition'],
+  'transition-duration': ['transition'],
+  'transition-property': ['transition'],
+  'transition-timing-function': ['transition'],
+  '-moz-transition-delay': ['-moz-transition'],
+  '-moz-transition-duration': ['-moz-transition'],
+  '-moz-transition-property': ['-moz-transition'],
+  '-moz-transition-timing-function': ['-moz-transition'],
+  '-o-transition-delay': ['-o-transition'],
+  '-o-transition-duration': ['-o-transition'],
+  '-o-transition-property': ['-o-transition'],
+  '-o-transition-timing-function': ['-o-transition'],
+  '-webkit-transition-delay': ['-webkit-transition'],
+  '-webkit-transition-duration': ['-webkit-transition'],
+  '-webkit-transition-property': ['-webkit-transition'],
+  '-webkit-transition-timing-function': ['-webkit-transition']
+};
+
+function _optimize(properties, mergeAdjacent, aggressiveMerging, validator) {
+  var overrideMapping = {};
+  var lastName = null;
+  var j;
+
+  function mergeablePosition(position) {
+    if (mergeAdjacent === false || mergeAdjacent === true)
+      return mergeAdjacent;
+
+    return mergeAdjacent.indexOf(position) > -1;
+  }
+
+  function sameValue(position) {
+    var left = properties[position - 1];
+    var right = properties[position];
+
+    return stringifyProperty(left.all, left.position) == stringifyProperty(right.all, right.position);
+  }
+
+  propertyLoop:
+  for (var position = 0, total = properties.length; position < total; position++) {
+    var property = properties[position];
+    var _name = (property.name == '-ms-filter' || property.name == 'filter') ?
+      (lastName == 'background' || lastName == 'background-image' ? lastName : property.name) :
+      property.name;
+    var isImportant = property.important;
+    var isHack = property.hack;
+
+    if (property.unused)
+      continue;
+
+    if (position > 0 && _name == lastName && sameValue(position)) {
+      property.unused = true;
+      continue;
+    }
+
+    // comment is necessary - we assume that if two properties are one after another
+    // then it is intentional way of redefining property which may not be widely supported
+    // e.g. a{display:inline-block;display:-moz-inline-box}
+    // however if `mergeablePosition` yields true then the rule does not apply
+    // (e.g merging two adjacent selectors: `a{display:block}a{display:block}`)
+    if (_name in overrideMapping && (aggressiveMerging && _name != lastName || mergeablePosition(position))) {
+      var toOverridePositions = overrideMapping[_name];
+      var canOverride = compactable[_name] && compactable[_name].canOverride;
+      var anyRemoved = false;
+
+      for (j = toOverridePositions.length - 1; j >= 0; j--) {
+        var toRemove = properties[toOverridePositions[j]];
+        var longhandToShorthand = toRemove.name != _name;
+        var wasImportant = toRemove.important;
+        var wasHack = toRemove.hack;
+
+        if (toRemove.unused)
+          continue;
+
+        if (longhandToShorthand && wasImportant)
+          continue;
+
+        if (!wasImportant && (wasHack && !isHack || !wasHack && isHack))
+          continue;
+
+        if (wasImportant && (isHack == 'star' || isHack == 'underscore'))
+          continue;
+
+        if (!wasHack && !isHack && !longhandToShorthand && canOverride && !canOverride(toRemove, property, validator))
+          continue;
+
+        if (wasImportant && !isImportant || wasImportant && isHack) {
+          property.unused = true;
+          continue propertyLoop;
+        } else {
+          anyRemoved = true;
+          toRemove.unused = true;
+        }
+      }
+
+      if (anyRemoved) {
+        position = -1;
+        overrideMapping = {};
+      }
+    } else {
+      overrideMapping[_name] = overrideMapping[_name] || [];
+      overrideMapping[_name].push(position);
+
+      // TODO: to be removed with
+      // certain shorthand (see values of `shorthands`) should trigger removal of
+      // longhand properties (see keys of `shorthands`)
+      var _shorthands = shorthands[_name];
+      if (_shorthands) {
+        for (j = _shorthands.length - 1; j >= 0; j--) {
+          var shorthand = _shorthands[j];
+          overrideMapping[shorthand] = overrideMapping[shorthand] || [];
+          overrideMapping[shorthand].push(position);
+        }
+      }
+    }
+
+    lastName = _name;
+  }
+}
+
+function optimize(selector, properties, mergeAdjacent, withCompacting, options, validator) {
+  var _properties = wrapForOptimizing(properties);
+  populateComponents(_properties, validator);
+  _optimize(_properties, mergeAdjacent, options.aggressiveMerging, validator);
+
+  for (var i = 0, l = _properties.length; i < l; i++) {
+    var _property = _properties[i];
+    if (_property.variable && _property.block)
+      optimize(selector, _property.value[0], mergeAdjacent, withCompacting, options, validator);
+  }
+
+  if (withCompacting && options.shorthandCompacting) {
+    compactOverrides(_properties, options.compatibility, validator);
+    compactShorthands(_properties, options.sourceMap, validator);
+  }
+
+  restoreFromOptimizing(_properties);
+  removeUnused(_properties);
+}
+
+module.exports = optimize;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/override-compactor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/override-compactor.js
new file mode 100644
index 0000000..c25ce79
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/override-compactor.js
@@ -0,0 +1,373 @@
+var canOverride = require('./can-override');
+var compactable = require('./compactable');
+var deepClone = require('./clone').deep;
+var shallowClone = require('./clone').shallow;
+var hasInherit = require('./has-inherit');
+var restoreFromOptimizing = require('./restore-from-optimizing');
+var everyCombination = require('./every-combination');
+var sameVendorPrefixesIn = require('./vendor-prefixes').same;
+
+var stringifyProperty = require('../stringifier/one-time').property;
+
+var MULTIPLEX_SEPARATOR = ',';
+
+// Used when searching for a component that matches property
+function nameMatchFilter(to) {
+  return function (property) {
+    return to.name === property.name;
+  };
+}
+
+function wouldBreakCompatibility(property, validator) {
+  for (var i = 0; i < property.components.length; i++) {
+    var component = property.components[i];
+    var descriptor = compactable[component.name];
+    var canOverride = descriptor && descriptor.canOverride || canOverride.sameValue;
+
+    var _component = shallowClone(component);
+    _component.value = [[descriptor.defaultValue]];
+
+    if (!canOverride(_component, component, validator))
+      return true;
+  }
+
+  return false;
+}
+
+function isComponentOf(shorthand, longhand) {
+  return compactable[shorthand.name].components.indexOf(longhand.name) > -1;
+}
+
+function overrideIntoMultiplex(property, by) {
+  by.unused = true;
+
+  turnIntoMultiplex(by, multiplexSize(property));
+  property.value = by.value;
+}
+
+function overrideByMultiplex(property, by) {
+  by.unused = true;
+  property.multiplex = true;
+  property.value = by.value;
+}
+
+function overrideSimple(property, by) {
+  by.unused = true;
+  property.value = by.value;
+}
+
+function override(property, by) {
+  if (by.multiplex)
+    overrideByMultiplex(property, by);
+  else if (property.multiplex)
+    overrideIntoMultiplex(property, by);
+  else
+    overrideSimple(property, by);
+}
+
+function overrideShorthand(property, by) {
+  by.unused = true;
+
+  for (var i = 0, l = property.components.length; i < l; i++) {
+    override(property.components[i], by.components[i], property.multiplex);
+  }
+}
+
+function turnIntoMultiplex(property, size) {
+  property.multiplex = true;
+
+  for (var i = 0, l = property.components.length; i < l; i++) {
+    var component = property.components[i];
+    if (component.multiplex)
+      continue;
+
+    var value = component.value.slice(0);
+
+    for (var j = 1; j < size; j++) {
+      component.value.push([MULTIPLEX_SEPARATOR]);
+      Array.prototype.push.apply(component.value, value);
+    }
+  }
+}
+
+function multiplexSize(component) {
+  var size = 0;
+
+  for (var i = 0, l = component.value.length; i < l; i++) {
+    if (component.value[i][0] == MULTIPLEX_SEPARATOR)
+      size++;
+  }
+
+  return size + 1;
+}
+
+function lengthOf(property) {
+  var fakeAsArray = [[property.name]].concat(property.value);
+  return stringifyProperty([fakeAsArray], 0).length;
+}
+
+function moreSameShorthands(properties, startAt, name) {
+  // Since we run the main loop in `compactOverrides` backwards, at this point some
+  // properties may not be marked as unused.
+  // We should consider reverting the order if possible
+  var count = 0;
+
+  for (var i = startAt; i >= 0; i--) {
+    if (properties[i].name == name && !properties[i].unused)
+      count++;
+    if (count > 1)
+      break;
+  }
+
+  return count > 1;
+}
+
+function mergingIntoFunction(left, right, validator) {
+  for (var i = 0, l = left.components.length; i < l; i++) {
+    if (anyValue(validator.isValidFunction, left.components[i]))
+      return true;
+  }
+
+  return false;
+}
+
+function anyValue(fn, property) {
+  for (var i = 0, l = property.value.length; i < l; i++) {
+    if (property.value[i][0] == MULTIPLEX_SEPARATOR)
+      continue;
+
+    if (fn(property.value[i][0]))
+      return true;
+  }
+
+  return false;
+}
+
+function wouldResultInLongerValue(left, right) {
+  if (!left.multiplex && !right.multiplex || left.multiplex && right.multiplex)
+    return false;
+
+  var multiplex = left.multiplex ? left : right;
+  var simple = left.multiplex ? right : left;
+  var component;
+
+  var multiplexClone = deepClone(multiplex);
+  restoreFromOptimizing([multiplexClone]);
+
+  var simpleClone = deepClone(simple);
+  restoreFromOptimizing([simpleClone]);
+
+  var lengthBefore = lengthOf(multiplexClone) + 1 + lengthOf(simpleClone);
+
+  if (left.multiplex) {
+    component = multiplexClone.components.filter(nameMatchFilter(simpleClone))[0];
+    overrideIntoMultiplex(component, simpleClone);
+  } else {
+    component = simpleClone.components.filter(nameMatchFilter(multiplexClone))[0];
+    turnIntoMultiplex(simpleClone, multiplexSize(multiplexClone));
+    overrideByMultiplex(component, multiplexClone);
+  }
+
+  restoreFromOptimizing([simpleClone]);
+
+  var lengthAfter = lengthOf(simpleClone);
+
+  return lengthBefore < lengthAfter;
+}
+
+function isCompactable(property) {
+  return property.name in compactable;
+}
+
+function noneOverrideHack(left, right) {
+  return !left.multiplex &&
+    (left.name == 'background' || left.name == 'background-image') &&
+    right.multiplex &&
+    (right.name == 'background' || right.name == 'background-image') &&
+    anyLayerIsNone(right.value);
+}
+
+function anyLayerIsNone(values) {
+  var layers = intoLayers(values);
+
+  for (var i = 0, l = layers.length; i < l; i++) {
+    if (layers[i].length == 1 && layers[i][0][0] == 'none')
+      return true;
+  }
+
+  return false;
+}
+
+function intoLayers(values) {
+  var layers = [];
+
+  for (var i = 0, layer = [], l = values.length; i < l; i++) {
+    var value = values[i];
+    if (value[0] == MULTIPLEX_SEPARATOR) {
+      layers.push(layer);
+      layer = [];
+    } else {
+      layer.push(value);
+    }
+  }
+
+  layers.push(layer);
+  return layers;
+}
+
+function compactOverrides(properties, compatibility, validator) {
+  var mayOverride, right, left, component;
+  var i, j, k;
+
+  propertyLoop:
+  for (i = properties.length - 1; i >= 0; i--) {
+    right = properties[i];
+
+    if (!isCompactable(right))
+      continue;
+
+    if (right.variable)
+      continue;
+
+    mayOverride = compactable[right.name].canOverride || canOverride.sameValue;
+
+    for (j = i - 1; j >= 0; j--) {
+      left = properties[j];
+
+      if (!isCompactable(left))
+        continue;
+
+      if (left.variable)
+        continue;
+
+      if (left.unused || right.unused)
+        continue;
+
+      if (left.hack && !right.hack || !left.hack && right.hack)
+        continue;
+
+      if (hasInherit(right))
+        continue;
+
+      if (noneOverrideHack(left, right))
+        continue;
+
+      if (!left.shorthand && right.shorthand && isComponentOf(right, left)) {
+        // maybe `left` can be overridden by `right` which is a shorthand?
+        if (!right.important && left.important)
+          continue;
+
+        if (!sameVendorPrefixesIn([left], right.components))
+          continue;
+
+        component = right.components.filter(nameMatchFilter(left))[0];
+        mayOverride = (compactable[left.name] && compactable[left.name].canOverride) || canOverride.sameValue;
+        if (everyCombination(mayOverride, left, component, validator)) {
+          left.unused = true;
+        }
+      } else if (left.shorthand && !right.shorthand && isComponentOf(left, right)) {
+        // maybe `right` can be pulled into `left` which is a shorthand?
+        if (right.important && !left.important)
+          continue;
+
+        // Pending more clever algorithm in #527
+        if (moreSameShorthands(properties, i - 1, left.name))
+          continue;
+
+        if (mergingIntoFunction(left, right, validator))
+          continue;
+
+        component = left.components.filter(nameMatchFilter(right))[0];
+        if (everyCombination(mayOverride, component, right, validator)) {
+          var disabledBackgroundMerging =
+            !compatibility.properties.backgroundClipMerging && component.name.indexOf('background-clip') > -1 ||
+            !compatibility.properties.backgroundOriginMerging && component.name.indexOf('background-origin') > -1 ||
+            !compatibility.properties.backgroundSizeMerging && component.name.indexOf('background-size') > -1;
+          var nonMergeableValue = compactable[right.name].nonMergeableValue === right.value[0][0];
+
+          if (disabledBackgroundMerging || nonMergeableValue)
+            continue;
+
+          if (!compatibility.properties.merging && wouldBreakCompatibility(left, validator))
+            continue;
+
+          if (component.value[0][0] != right.value[0][0] && (hasInherit(left) || hasInherit(right)))
+            continue;
+
+          if (wouldResultInLongerValue(left, right))
+            continue;
+
+          if (!left.multiplex && right.multiplex)
+            turnIntoMultiplex(left, multiplexSize(right));
+
+          override(component, right);
+          left.dirty = true;
+        }
+      } else if (left.shorthand && right.shorthand && left.name == right.name) {
+        // merge if all components can be merged
+
+        if (!right.important && left.important) {
+          right.unused = true;
+          continue propertyLoop;
+        }
+
+        if (right.important && !left.important) {
+          left.unused = true;
+          continue;
+        }
+
+        for (k = left.components.length - 1; k >= 0; k--) {
+          var leftComponent = left.components[k];
+          var rightComponent = right.components[k];
+
+          mayOverride = compactable[leftComponent.name].canOverride || canOverride.sameValue;
+          if (!everyCombination(mayOverride, leftComponent, rightComponent, validator))
+            continue propertyLoop;
+          if (!everyCombination(canOverride.twoOptionalFunctions, leftComponent, rightComponent, validator) && validator.isValidFunction(rightComponent))
+            continue propertyLoop;
+        }
+
+        overrideShorthand(left, right);
+        left.dirty = true;
+      } else if (left.shorthand && right.shorthand && isComponentOf(left, right)) {
+        // border is a shorthand but any of its components is a shorthand too
+
+        if (!left.important && right.important)
+          continue;
+
+        component = left.components.filter(nameMatchFilter(right))[0];
+        mayOverride = compactable[right.name].canOverride || canOverride.sameValue;
+        if (!everyCombination(mayOverride, component, right, validator))
+          continue;
+
+        if (left.important && !right.important) {
+          right.unused = true;
+          continue;
+        }
+
+        var rightRestored = compactable[right.name].restore(right, compactable);
+        if (rightRestored.length > 1)
+          continue;
+
+        component = left.components.filter(nameMatchFilter(right))[0];
+        override(component, right);
+        right.dirty = true;
+      } else if (left.name == right.name) {
+        // two non-shorthands should be merged based on understandability
+
+        if (left.important && !right.important) {
+          right.unused = true;
+          continue;
+        }
+
+        mayOverride = compactable[right.name].canOverride || canOverride.sameValue;
+        if (!everyCombination(mayOverride, left, right, validator))
+          continue;
+
+        left.unused = true;
+      }
+    }
+  }
+}
+
+module.exports = compactOverrides;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/populate-components.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/populate-components.js
new file mode 100644
index 0000000..3093071
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/populate-components.js
@@ -0,0 +1,21 @@
+var compactable = require('./compactable');
+
+function populateComponents(properties, validator) {
+  for (var i = properties.length - 1; i >= 0; i--) {
+    var property = properties[i];
+    var descriptor = compactable[property.name];
+
+    if (descriptor && descriptor.shorthand) {
+      property.shorthand = true;
+      property.dirty = true;
+      property.components = descriptor.breakUp(property, compactable, validator);
+
+      if (property.components.length > 0)
+        property.multiplex = property.components[0].multiplex;
+      else
+        property.unused = true;
+    }
+  }
+}
+
+module.exports = populateComponents;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/remove-unused.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/remove-unused.js
new file mode 100644
index 0000000..08cb9b6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/remove-unused.js
@@ -0,0 +1,10 @@
+function removeUnused(properties) {
+  for (var i = properties.length - 1; i >= 0; i--) {
+    var property = properties[i];
+
+    if (property.unused)
+      property.all.splice(property.position, 1);
+  }
+}
+
+module.exports = removeUnused;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/restore-from-optimizing.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/restore-from-optimizing.js
new file mode 100644
index 0000000..f0dfc18
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/restore-from-optimizing.js
@@ -0,0 +1,60 @@
+var compactable = require('./compactable');
+
+var BACKSLASH_HACK = '\\9';
+var IMPORTANT_TOKEN = '!important';
+var STAR_HACK = '*';
+var UNDERSCORE_HACK = '_';
+var BANG_HACK = '!ie';
+
+function restoreImportant(property) {
+  property.value[property.value.length - 1][0] += IMPORTANT_TOKEN;
+}
+
+function restoreHack(property) {
+  if (property.hack == 'underscore')
+    property.name = UNDERSCORE_HACK + property.name;
+  else if (property.hack == 'star')
+    property.name = STAR_HACK + property.name;
+  else if (property.hack == 'backslash')
+    property.value[property.value.length - 1][0] += BACKSLASH_HACK;
+  else if (property.hack == 'bang')
+    property.value[property.value.length - 1][0] += ' ' + BANG_HACK;
+}
+
+function restoreFromOptimizing(properties, simpleMode) {
+  for (var i = properties.length - 1; i >= 0; i--) {
+    var property = properties[i];
+    var descriptor = compactable[property.name];
+    var restored;
+
+    if (property.unused)
+      continue;
+
+    if (!property.dirty && !property.important && !property.hack)
+      continue;
+
+    if (!simpleMode && descriptor && descriptor.shorthand) {
+      restored = descriptor.restore(property, compactable);
+      property.value = restored;
+    } else {
+      restored = property.value;
+    }
+
+    if (property.important)
+      restoreImportant(property);
+
+    if (property.hack)
+      restoreHack(property);
+
+    if (!('all' in property))
+      continue;
+
+    var current = property.all[property.position];
+    current[0][0] = property.name;
+
+    current.splice(1, current.length - 1);
+    Array.prototype.push.apply(current, restored);
+  }
+}
+
+module.exports = restoreFromOptimizing;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/restore.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/restore.js
new file mode 100644
index 0000000..d9d40f9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/restore.js
@@ -0,0 +1,229 @@
+var shallowClone = require('./clone').shallow;
+var MULTIPLEX_SEPARATOR = ',';
+var SIZE_POSITION_SEPARATOR = '/';
+
+function isInheritOnly(values) {
+  for (var i = 0, l = values.length; i < l; i++) {
+    var value = values[i][0];
+
+    if (value != 'inherit' && value != MULTIPLEX_SEPARATOR && value != SIZE_POSITION_SEPARATOR)
+      return false;
+  }
+
+  return true;
+}
+
+function background(property, compactable, lastInMultiplex) {
+  var components = property.components;
+  var restored = [];
+  var needsOne, needsBoth;
+
+  function restoreValue(component) {
+    Array.prototype.unshift.apply(restored, component.value);
+  }
+
+  function isDefaultValue(component) {
+    var descriptor = compactable[component.name];
+    if (descriptor.doubleValues) {
+      if (descriptor.defaultValue.length == 1)
+        return component.value[0][0] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][0] == descriptor.defaultValue[0] : true);
+      else
+        return component.value[0][0] == descriptor.defaultValue[0] && (component.value[1] ? component.value[1][0] : component.value[0][0]) == descriptor.defaultValue[1];
+    } else {
+      return component.value[0][0] == descriptor.defaultValue;
+    }
+  }
+
+  for (var i = components.length - 1; i >= 0; i--) {
+    var component = components[i];
+    var isDefault = isDefaultValue(component);
+
+    if (component.name == 'background-clip') {
+      var originComponent = components[i - 1];
+      var isOriginDefault = isDefaultValue(originComponent);
+
+      needsOne = component.value[0][0] == originComponent.value[0][0];
+
+      needsBoth = !needsOne && (
+        (isOriginDefault && !isDefault) ||
+        (!isOriginDefault && !isDefault) ||
+        (!isOriginDefault && isDefault && component.value[0][0] != originComponent.value[0][0]));
+
+      if (needsOne) {
+        restoreValue(originComponent);
+      } else if (needsBoth) {
+        restoreValue(component);
+        restoreValue(originComponent);
+      }
+
+      i--;
+    } else if (component.name == 'background-size') {
+      var positionComponent = components[i - 1];
+      var isPositionDefault = isDefaultValue(positionComponent);
+
+      needsOne = !isPositionDefault && isDefault;
+
+      needsBoth = !needsOne &&
+        (isPositionDefault && !isDefault || !isPositionDefault && !isDefault);
+
+      if (needsOne) {
+        restoreValue(positionComponent);
+      } else if (needsBoth) {
+        restoreValue(component);
+        restored.unshift([SIZE_POSITION_SEPARATOR]);
+        restoreValue(positionComponent);
+      } else if (positionComponent.value.length == 1) {
+        restoreValue(positionComponent);
+      }
+
+      i--;
+    } else {
+      if (isDefault || compactable[component.name].multiplexLastOnly && !lastInMultiplex)
+        continue;
+
+      restoreValue(component);
+    }
+  }
+
+  if (restored.length === 0 && property.value.length == 1 && property.value[0][0] == '0')
+    restored.push(property.value[0]);
+
+  if (restored.length === 0)
+    restored.push([compactable[property.name].defaultValue]);
+
+  if (isInheritOnly(restored))
+    return [restored[0]];
+
+  return restored;
+}
+
+function borderRadius(property, compactable) {
+  if (property.multiplex) {
+    var horizontal = shallowClone(property);
+    var vertical = shallowClone(property);
+
+    for (var i = 0; i < 4; i++) {
+      var component = property.components[i];
+
+      var horizontalComponent = shallowClone(property);
+      horizontalComponent.value = [component.value[0]];
+      horizontal.components.push(horizontalComponent);
+
+      var verticalComponent = shallowClone(property);
+      verticalComponent.value = [component.value[2]];
+      vertical.components.push(verticalComponent);
+    }
+
+    var horizontalValues = fourValues(horizontal, compactable);
+    var verticalValues = fourValues(vertical, compactable);
+
+    if (horizontalValues.length == verticalValues.length &&
+        horizontalValues[0][0] == verticalValues[0][0] &&
+        (horizontalValues.length > 1 ? horizontalValues[1][0] == verticalValues[1][0] : true) &&
+        (horizontalValues.length > 2 ? horizontalValues[2][0] == verticalValues[2][0] : true) &&
+        (horizontalValues.length > 3 ? horizontalValues[3][0] == verticalValues[3][0] : true)) {
+      return horizontalValues;
+    } else {
+      return horizontalValues.concat([['/']]).concat(verticalValues);
+    }
+  } else {
+    return fourValues(property, compactable);
+  }
+}
+
+function fourValues(property) {
+  var components = property.components;
+  var value1 = components[0].value[0];
+  var value2 = components[1].value[0];
+  var value3 = components[2].value[0];
+  var value4 = components[3].value[0];
+
+  if (value1[0] == value2[0] && value1[0] == value3[0] && value1[0] == value4[0]) {
+    return [value1];
+  } else if (value1[0] == value3[0] && value2[0] == value4[0]) {
+    return [value1, value2];
+  } else if (value2[0] == value4[0]) {
+    return [value1, value2, value3];
+  } else {
+    return [value1, value2, value3, value4];
+  }
+}
+
+function multiplex(restoreWith) {
+  return function (property, compactable) {
+    if (!property.multiplex)
+      return restoreWith(property, compactable, true);
+
+    var multiplexSize = 0;
+    var restored = [];
+    var componentMultiplexSoFar = {};
+    var i, l;
+
+    // At this point we don't know what's the multiplex size, e.g. how many background layers are there
+    for (i = 0, l = property.components[0].value.length; i < l; i++) {
+      if (property.components[0].value[i][0] == MULTIPLEX_SEPARATOR)
+        multiplexSize++;
+    }
+
+    for (i = 0; i <= multiplexSize; i++) {
+      var _property = shallowClone(property);
+
+      // We split multiplex into parts and restore them one by one
+      for (var j = 0, m = property.components.length; j < m; j++) {
+        var componentToClone = property.components[j];
+        var _component = shallowClone(componentToClone);
+        _property.components.push(_component);
+
+        // The trick is some properties has more than one value, so we iterate over values looking for
+        // a multiplex separator - a comma
+        for (var k = componentMultiplexSoFar[_component.name] || 0, n = componentToClone.value.length; k < n; k++) {
+          if (componentToClone.value[k][0] == MULTIPLEX_SEPARATOR) {
+            componentMultiplexSoFar[_component.name] = k + 1;
+            break;
+          }
+
+          _component.value.push(componentToClone.value[k]);
+        }
+      }
+
+      // No we can restore shorthand value
+      var lastInMultiplex = i == multiplexSize;
+      var _restored = restoreWith(_property, compactable, lastInMultiplex);
+      Array.prototype.push.apply(restored, _restored);
+
+      if (i < multiplexSize)
+        restored.push([',']);
+    }
+
+    return restored;
+  };
+}
+
+function withoutDefaults(property, compactable) {
+  var components = property.components;
+  var restored = [];
+
+  for (var i = components.length - 1; i >= 0; i--) {
+    var component = components[i];
+    var descriptor = compactable[component.name];
+
+    if (component.value[0][0] != descriptor.defaultValue)
+      restored.unshift(component.value[0]);
+  }
+
+  if (restored.length === 0)
+    restored.push([compactable[property.name].defaultValue]);
+
+  if (isInheritOnly(restored))
+    return [restored[0]];
+
+  return restored;
+}
+
+module.exports = {
+  background: background,
+  borderRadius: borderRadius,
+  fourValues: fourValues,
+  multiplex: multiplex,
+  withoutDefaults: withoutDefaults
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/shorthand-compactor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/shorthand-compactor.js
new file mode 100644
index 0000000..1cd48f6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/shorthand-compactor.js
@@ -0,0 +1,134 @@
+var compactable = require('./compactable');
+var deepClone = require('./clone').deep;
+var hasInherit = require('./has-inherit');
+var populateComponents = require('./populate-components');
+var wrapSingle = require('./wrap-for-optimizing').single;
+var everyCombination = require('./every-combination');
+
+function mixedImportance(components) {
+  var important;
+
+  for (var name in components) {
+    if (undefined !== important && components[name].important != important)
+      return true;
+
+    important = components[name].important;
+  }
+
+  return false;
+}
+
+function componentSourceMaps(components) {
+  var sourceMapping = [];
+
+  for (var name in components) {
+    var component = components[name];
+    var originalValue = component.all[component.position];
+    var mapping = originalValue[0][originalValue[0].length - 1];
+
+    if (Array.isArray(mapping))
+      Array.prototype.push.apply(sourceMapping, mapping);
+  }
+
+  return sourceMapping;
+}
+
+function replaceWithShorthand(properties, candidateComponents, name, sourceMaps, validator) {
+  var descriptor = compactable[name];
+  var newValuePlaceholder = [[name], [descriptor.defaultValue]];
+  var all;
+
+  var newProperty = wrapSingle(newValuePlaceholder);
+  newProperty.shorthand = true;
+  newProperty.dirty = true;
+
+  populateComponents([newProperty], validator);
+
+  for (var i = 0, l = descriptor.components.length; i < l; i++) {
+    var component = candidateComponents[descriptor.components[i]];
+    var canOverride = compactable[component.name].canOverride;
+
+    if (hasInherit(component))
+      return;
+
+    if (!everyCombination(canOverride, newProperty.components[i], component, validator))
+      return;
+
+    newProperty.components[i] = deepClone(component);
+    newProperty.important = component.important;
+
+    all = component.all;
+  }
+
+  for (var componentName in candidateComponents) {
+    candidateComponents[componentName].unused = true;
+  }
+
+  if (sourceMaps) {
+    var sourceMapping = componentSourceMaps(candidateComponents);
+    if (sourceMapping.length > 0)
+      newValuePlaceholder[0].push(sourceMapping);
+  }
+
+  newProperty.position = all.length;
+  newProperty.all = all;
+  newProperty.all.push(newValuePlaceholder);
+
+  properties.push(newProperty);
+}
+
+function invalidateOrCompact(properties, position, candidates, sourceMaps, validator) {
+  var property = properties[position];
+
+  for (var name in candidates) {
+    if (undefined !== property && name == property.name)
+      continue;
+
+    var descriptor = compactable[name];
+    var candidateComponents = candidates[name];
+    if (descriptor.components.length > Object.keys(candidateComponents).length) {
+      delete candidates[name];
+      continue;
+    }
+
+    if (mixedImportance(candidateComponents))
+      continue;
+
+    replaceWithShorthand(properties, candidateComponents, name, sourceMaps, validator);
+  }
+}
+
+function compactShortands(properties, sourceMaps, validator) {
+  var candidates = {};
+
+  if (properties.length < 3)
+    return;
+
+  for (var i = 0, l = properties.length; i < l; i++) {
+    var property = properties[i];
+    if (property.unused)
+      continue;
+
+    if (property.hack)
+      continue;
+
+    if (property.variable)
+      continue;
+
+    var descriptor = compactable[property.name];
+    if (!descriptor || !descriptor.componentOf)
+      continue;
+
+    if (property.shorthand) {
+      invalidateOrCompact(properties, i, candidates, sourceMaps, validator);
+    } else {
+      var componentOf = descriptor.componentOf;
+      candidates[componentOf] = candidates[componentOf] || {};
+      candidates[componentOf][property.name] = property;
+    }
+  }
+
+  invalidateOrCompact(properties, i, candidates, sourceMaps, validator);
+}
+
+module.exports = compactShortands;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/validator.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/validator.js
new file mode 100644
index 0000000..2e8356c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/validator.js
@@ -0,0 +1,183 @@
+// Validates various CSS property values
+
+var split = require('../utils/split');
+
+var widthKeywords = ['thin', 'thick', 'medium', 'inherit', 'initial'];
+var allUnits = ['px', '%', 'em', 'in', 'cm', 'mm', 'ex', 'pt', 'pc', 'ch', 'rem', 'vh', 'vm', 'vmin', 'vmax', 'vw'];
+var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + allUnits.join('|') + '|)|auto|inherit)';
+var cssCalcRegexStr = '(\\-moz\\-|\\-webkit\\-)?calc\\([^\\)]+\\)';
+var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(.*?\\)';
+var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(.*?\\)';
+var cssVariableRegexStr = 'var\\(\\-\\-[^\\)]+\\)';
+var cssFunctionAnyRegexStr = '(' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
+var cssUnitOrCalcRegexStr = '(' + cssUnitRegexStr + '|' + cssCalcRegexStr + ')';
+var cssUnitAnyRegexStr = '(none|' + widthKeywords.join('|') + '|' + cssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
+
+var cssFunctionNoVendorRegex = new RegExp('^' + cssFunctionNoVendorRegexStr + '$', 'i');
+var cssFunctionVendorRegex = new RegExp('^' + cssFunctionVendorRegexStr + '$', 'i');
+var cssVariableRegex = new RegExp('^' + cssVariableRegexStr + '$', 'i');
+var cssFunctionAnyRegex = new RegExp('^' + cssFunctionAnyRegexStr + '$', 'i');
+var cssUnitRegex = new RegExp('^' + cssUnitRegexStr + '$', 'i');
+var cssUnitOrCalcRegex = new RegExp('^' + cssUnitOrCalcRegexStr + '$', 'i');
+var cssUnitAnyRegex = new RegExp('^' + cssUnitAnyRegexStr + '$', 'i');
+
+var backgroundRepeatKeywords = ['repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'inherit'];
+var backgroundAttachmentKeywords = ['inherit', 'scroll', 'fixed', 'local'];
+var backgroundPositionKeywords = ['center', 'top', 'bottom', 'left', 'right'];
+var backgroundSizeKeywords = ['contain', 'cover'];
+var backgroundBoxKeywords = ['border-box', 'content-box', 'padding-box'];
+var styleKeywords = ['auto', 'inherit', 'hidden', 'none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
+var listStyleTypeKeywords = ['armenian', 'circle', 'cjk-ideographic', 'decimal', 'decimal-leading-zero', 'disc', 'georgian', 'hebrew', 'hiragana', 'hiragana-iroha', 'inherit', 'katakana', 'katakana-iroha', 'lower-alpha', 'lower-greek', 'lower-latin', 'lower-roman', 'none', 'square', 'upper-alpha', 'upper-latin', 'upper-roman'];
+var listStylePositionKeywords = ['inside', 'outside', 'inherit'];
+
+function Validator(compatibility) {
+  var validUnits = allUnits.slice(0).filter(function (value) {
+    return !(value in compatibility.units) || compatibility.units[value] === true;
+  });
+
+  var compatibleCssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(' + validUnits.join('|') + '|)|auto|inherit)';
+  this.compatibleCssUnitRegex = new RegExp('^' + compatibleCssUnitRegexStr + '$', 'i');
+  this.compatibleCssUnitAnyRegex = new RegExp('^(none|' + widthKeywords.join('|') + '|' + compatibleCssUnitRegexStr + '|' + cssVariableRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')$', 'i');
+}
+
+Validator.prototype.isValidHexColor = function (s) {
+  return (s.length === 4 || s.length === 7) && s[0] === '#';
+};
+
+Validator.prototype.isValidRgbaColor = function (s) {
+  s = s.split(' ').join('');
+  return s.length > 0 && s.indexOf('rgba(') === 0 && s.indexOf(')') === s.length - 1;
+};
+
+Validator.prototype.isValidHslaColor = function (s) {
+  s = s.split(' ').join('');
+  return s.length > 0 && s.indexOf('hsla(') === 0 && s.indexOf(')') === s.length - 1;
+};
+
+Validator.prototype.isValidNamedColor = function (s) {
+  // We don't really check if it's a valid color value, but allow any letters in it
+  return s !== 'auto' && (s === 'transparent' || s === 'inherit' || /^[a-zA-Z]+$/.test(s));
+};
+
+Validator.prototype.isValidVariable = function (s) {
+  return cssVariableRegex.test(s);
+};
+
+Validator.prototype.isValidColor = function (s) {
+  return this.isValidNamedColor(s) ||
+    this.isValidHexColor(s) ||
+    this.isValidRgbaColor(s) ||
+    this.isValidHslaColor(s) ||
+    this.isValidVariable(s) ||
+    this.isValidVendorPrefixedValue(s);
+};
+
+Validator.prototype.isValidUrl = function (s) {
+  // NOTE: at this point all URLs are replaced with placeholders by clean-css, so we check for those placeholders
+  return s.indexOf('__ESCAPED_URL_CLEAN_CSS') === 0;
+};
+
+Validator.prototype.isValidUnit = function (s) {
+  return cssUnitAnyRegex.test(s);
+};
+
+Validator.prototype.isValidUnitWithoutFunction = function (s) {
+  return cssUnitRegex.test(s);
+};
+
+Validator.prototype.isValidAndCompatibleUnit = function (s) {
+  return this.compatibleCssUnitAnyRegex.test(s);
+};
+
+Validator.prototype.isValidAndCompatibleUnitWithoutFunction = function (s) {
+  return this.compatibleCssUnitRegex.test(s);
+};
+
+Validator.prototype.isValidFunctionWithoutVendorPrefix = function (s) {
+  return cssFunctionNoVendorRegex.test(s);
+};
+
+Validator.prototype.isValidFunctionWithVendorPrefix = function (s) {
+  return cssFunctionVendorRegex.test(s);
+};
+
+Validator.prototype.isValidFunction = function (s) {
+  return cssFunctionAnyRegex.test(s);
+};
+
+Validator.prototype.isValidBackgroundRepeat = function (s) {
+  return backgroundRepeatKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundAttachment = function (s) {
+  return backgroundAttachmentKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundBox = function (s) {
+  return backgroundBoxKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundPositionPart = function (s) {
+  return backgroundPositionKeywords.indexOf(s) >= 0 || cssUnitOrCalcRegex.test(s) || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundPosition = function (s) {
+  if (s === 'inherit')
+    return true;
+
+  var parts = s.split(' ');
+  for (var i = 0, l = parts.length; i < l; i++) {
+    if (parts[i] === '')
+      continue;
+    if (this.isValidBackgroundPositionPart(parts[i]) || this.isValidVariable(parts[i]))
+      continue;
+
+    return false;
+  }
+
+  return true;
+};
+
+Validator.prototype.isValidBackgroundSizePart = function (s) {
+  return backgroundSizeKeywords.indexOf(s) >= 0 || cssUnitRegex.test(s) || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidBackgroundPositionAndSize = function (s) {
+  if (s.indexOf('/') < 0)
+    return false;
+
+  var twoParts = split(s, '/');
+  return this.isValidBackgroundSizePart(twoParts.pop()) && this.isValidBackgroundPositionPart(twoParts.pop());
+};
+
+Validator.prototype.isValidListStyleType = function (s) {
+  return listStyleTypeKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidListStylePosition = function (s) {
+  return listStylePositionKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidStyle = function (s) {
+  return styleKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidWidth = function (s) {
+  return this.isValidUnit(s) || widthKeywords.indexOf(s) >= 0 || this.isValidVariable(s);
+};
+
+Validator.prototype.isValidVendorPrefixedValue = function (s) {
+  return /^-([A-Za-z0-9]|-)*$/gi.test(s);
+};
+
+Validator.prototype.areSameFunction = function (a, b) {
+  if (!this.isValidFunction(a) || !this.isValidFunction(b))
+    return false;
+
+  var f1name = a.substring(0, a.indexOf('('));
+  var f2name = b.substring(0, b.indexOf('('));
+
+  return f1name === f2name;
+};
+
+module.exports = Validator;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/vendor-prefixes.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/vendor-prefixes.js
new file mode 100644
index 0000000..511fd60
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/vendor-prefixes.js
@@ -0,0 +1,26 @@
+var VENDOR_PREFIX_PATTERN = /$\-moz\-|\-ms\-|\-o\-|\-webkit\-/;
+
+function prefixesIn(tokens) {
+  var prefixes = [];
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    for (var j = 0, m = token.value.length; j < m; j++) {
+      var match = VENDOR_PREFIX_PATTERN.exec(token.value[j][0]);
+
+      if (match && prefixes.indexOf(match[0]) == -1)
+        prefixes.push(match[0]);
+    }
+  }
+
+  return prefixes;
+}
+
+function same(left, right) {
+  return prefixesIn(left).sort().join(',') == prefixesIn(right).sort().join(',');
+}
+
+module.exports = {
+  same: same
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/wrap-for-optimizing.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/wrap-for-optimizing.js
new file mode 100644
index 0000000..4de3c64
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/properties/wrap-for-optimizing.js
@@ -0,0 +1,111 @@
+var BACKSLASH_HACK = '\\';
+var IMPORTANT_TOKEN = '!important';
+var STAR_HACK = '*';
+var UNDERSCORE_HACK = '_';
+var BANG_HACK = '!';
+
+function wrapAll(properties) {
+  var wrapped = [];
+
+  for (var i = properties.length - 1; i >= 0; i--) {
+    if (typeof properties[i][0] == 'string')
+      continue;
+
+    var single = wrapSingle(properties[i]);
+    single.all = properties;
+    single.position = i;
+    wrapped.unshift(single);
+  }
+
+  return wrapped;
+}
+
+function isMultiplex(property) {
+  for (var i = 1, l = property.length; i < l; i++) {
+    if (property[i][0] == ',' || property[i][0] == '/')
+      return true;
+  }
+
+  return false;
+}
+
+function hackType(property) {
+  var type = false;
+  var name = property[0][0];
+  var lastValue = property[property.length - 1];
+
+  if (name[0] == UNDERSCORE_HACK) {
+    type = 'underscore';
+  } else if (name[0] == STAR_HACK) {
+    type = 'star';
+  } else if (lastValue[0][0] == BANG_HACK && lastValue[0].indexOf('important') == -1) {
+    type = 'bang';
+  } else if (lastValue[0].indexOf(BANG_HACK) > 0 && lastValue[0].indexOf('important') == -1) {
+    type = 'bang';
+  } else if (lastValue[0].indexOf(BACKSLASH_HACK) > 0 && lastValue[0].indexOf(BACKSLASH_HACK) == lastValue[0].length - BACKSLASH_HACK.length - 1) {
+    type = 'backslash';
+  } else if (lastValue[0].indexOf(BACKSLASH_HACK) === 0 && lastValue[0].length == 2) {
+    type = 'backslash';
+  }
+
+  return type;
+}
+
+function isImportant(property) {
+  return property.length > 1 ?
+    property[property.length - 1][0].indexOf(IMPORTANT_TOKEN) > 0 :
+    false;
+}
+
+function stripImportant(property) {
+  if (property.length > 0)
+    property[property.length - 1][0] = property[property.length - 1][0].replace(IMPORTANT_TOKEN, '');
+}
+
+function stripPrefixHack(property) {
+  property[0][0] = property[0][0].substring(1);
+}
+
+function stripSuffixHack(property, hackType) {
+  var lastValue = property[property.length - 1];
+  lastValue[0] = lastValue[0]
+    .substring(0, lastValue[0].indexOf(hackType == 'backslash' ? BACKSLASH_HACK : BANG_HACK))
+    .trim();
+
+  if (lastValue[0].length === 0)
+    property.pop();
+}
+
+function wrapSingle(property) {
+  var _isImportant = isImportant(property);
+  if (_isImportant)
+    stripImportant(property);
+
+  var _hackType = hackType(property);
+  if (_hackType == 'star' || _hackType == 'underscore')
+    stripPrefixHack(property);
+  else if (_hackType == 'backslash' || _hackType == 'bang')
+    stripSuffixHack(property, _hackType);
+
+  var isVariable = property[0][0].indexOf('--') === 0;
+
+  return {
+    block: isVariable && property[1] && Array.isArray(property[1][0][0]),
+    components: [],
+    dirty: false,
+    hack: _hackType,
+    important: _isImportant,
+    name: property[0][0],
+    multiplex: property.length > 2 ? isMultiplex(property) : false,
+    position: 0,
+    shorthand: false,
+    unused: property.length < 2,
+    value: property.slice(1),
+    variable: isVariable
+  };
+}
+
+module.exports = {
+  all: wrapAll,
+  single: wrapSingle
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/advanced.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/advanced.js
new file mode 100644
index 0000000..f03b11f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/advanced.js
@@ -0,0 +1,86 @@
+var optimizeProperties = require('../properties/optimizer');
+
+var removeDuplicates = require('./remove-duplicates');
+var mergeAdjacent = require('./merge-adjacent');
+var reduceNonAdjacent = require('./reduce-non-adjacent');
+var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
+var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body');
+var restructure = require('./restructure');
+var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries');
+var mergeMediaQueries = require('./merge-media-queries');
+
+function removeEmpty(tokens) {
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+    var isEmpty = false;
+
+    switch (token[0]) {
+      case 'selector':
+        isEmpty = token[1].length === 0 || token[2].length === 0;
+        break;
+      case 'block':
+        removeEmpty(token[2]);
+        isEmpty = token[2].length === 0;
+    }
+
+    if (isEmpty) {
+      tokens.splice(i, 1);
+      i--;
+      l--;
+    }
+  }
+}
+
+function recursivelyOptimizeBlocks(tokens, options, validator) {
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    if (token[0] == 'block') {
+      var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0]);
+      optimize(token[2], options, validator, !isKeyframes);
+    }
+  }
+}
+
+function recursivelyOptimizeProperties(tokens, options, validator) {
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    switch (token[0]) {
+      case 'selector':
+        optimizeProperties(token[1], token[2], false, true, options, validator);
+        break;
+      case 'block':
+        recursivelyOptimizeProperties(token[2], options, validator);
+    }
+  }
+}
+
+function optimize(tokens, options, validator, withRestructuring) {
+  recursivelyOptimizeBlocks(tokens, options, validator);
+  recursivelyOptimizeProperties(tokens, options, validator);
+
+  removeDuplicates(tokens);
+  mergeAdjacent(tokens, options, validator);
+  reduceNonAdjacent(tokens, options, validator);
+
+  mergeNonAdjacentBySelector(tokens, options, validator);
+  mergeNonAdjacentByBody(tokens, options);
+
+  if (options.restructuring && withRestructuring) {
+    restructure(tokens, options);
+    mergeAdjacent(tokens, options, validator);
+  }
+
+  if (options.mediaMerging) {
+    removeDuplicateMediaQueries(tokens);
+    var reduced = mergeMediaQueries(tokens);
+    for (var i = reduced.length - 1; i >= 0; i--) {
+      optimize(reduced[i][2], options, validator, false);
+    }
+  }
+
+  removeEmpty(tokens);
+}
+
+module.exports = optimize;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/clean-up.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/clean-up.js
new file mode 100644
index 0000000..3c105ec
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/clean-up.js
@@ -0,0 +1,80 @@
+function removeWhitespace(match, value) {
+  return '[' + value.replace(/ /g, '') + ']';
+}
+
+function selectorSorter(s1, s2) {
+  return s1[0] > s2[0] ? 1 : -1;
+}
+
+var CleanUp = {
+  selectors: function (selectors, removeUnsupported, adjacentSpace) {
+    var list = [];
+    var repeated = [];
+
+    for (var i = 0, l = selectors.length; i < l; i++) {
+      var selector = selectors[i];
+      var reduced = selector[0]
+        .replace(/\s+/g, ' ')
+        .replace(/ ?, ?/g, ',')
+        .replace(/\s*([>\+\~])\s*/g, '$1')
+        .trim();
+
+      if (adjacentSpace && reduced.indexOf('nav') > 0)
+        reduced = reduced.replace(/\+nav(\S|$)/, '+ nav$1');
+
+      if (removeUnsupported && (reduced.indexOf('*+html ') != -1 || reduced.indexOf('*:first-child+html ') != -1))
+        continue;
+
+      if (reduced.indexOf('*') > -1) {
+        reduced = reduced
+          .replace(/\*([:#\.\[])/g, '$1')
+          .replace(/^(\:first\-child)?\+html/, '*$1+html');
+      }
+
+      if (reduced.indexOf('[') > -1)
+        reduced = reduced.replace(/\[([^\]]+)\]/g, removeWhitespace);
+
+      if (repeated.indexOf(reduced) == -1) {
+        selector[0] = reduced;
+        repeated.push(reduced);
+        list.push(selector);
+      }
+    }
+
+    return list.sort(selectorSorter);
+  },
+
+  selectorDuplicates: function (selectors) {
+    var list = [];
+    var repeated = [];
+
+    for (var i = 0, l = selectors.length; i < l; i++) {
+      var selector = selectors[i];
+
+      if (repeated.indexOf(selector[0]) == -1) {
+        repeated.push(selector[0]);
+        list.push(selector);
+      }
+    }
+
+    return list.sort(selectorSorter);
+  },
+
+  block: function (values, spaceAfterClosingBrace) {
+    values[0] = values[0]
+      .replace(/\s+/g, ' ')
+      .replace(/(,|:|\() /g, '$1')
+      .replace(/ \)/g, ')');
+
+    if (!spaceAfterClosingBrace)
+      values[0] = values[0].replace(/\) /g, ')');
+  },
+
+  atRule: function (values) {
+    values[0] = values[0]
+      .replace(/\s+/g, ' ')
+      .trim();
+  }
+};
+
+module.exports = CleanUp;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/extractor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/extractor.js
new file mode 100644
index 0000000..497198b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/extractor.js
@@ -0,0 +1,69 @@
+// This extractor is used in advanced optimizations
+// IMPORTANT: Mind Token class and this code is not related!
+// Properties will be tokenized in one step, see #429
+
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var stringifyValue = require('../stringifier/one-time').value;
+
+var AT_RULE = 'at-rule';
+
+function extract(token) {
+  var properties = [];
+
+  if (token[0] == 'selector') {
+    var inSpecificSelector = !/[\.\+>~]/.test(stringifySelectors(token[1]));
+    for (var i = 0, l = token[2].length; i < l; i++) {
+      var property = token[2][i];
+
+      if (property.indexOf('__ESCAPED') === 0)
+        continue;
+
+      if (property[0] == AT_RULE)
+        continue;
+
+      var name = token[2][i][0][0];
+      if (name.length === 0)
+        continue;
+
+      if (name.indexOf('--') === 0)
+        continue;
+
+      var value = stringifyValue(token[2], i);
+
+      properties.push([
+        name,
+        value,
+        findNameRoot(name),
+        token[2][i],
+        name + ':' + value,
+        token[1],
+        inSpecificSelector
+      ]);
+    }
+  } else if (token[0] == 'block') {
+    for (var j = 0, k = token[2].length; j < k; j++) {
+      properties = properties.concat(extract(token[2][j]));
+    }
+  }
+
+  return properties;
+}
+
+function findNameRoot(name) {
+  if (name == 'list-style')
+    return name;
+  if (name.indexOf('-radius') > 0)
+    return 'border-radius';
+  if (name == 'border-collapse' || name == 'border-spacing' || name == 'border-image')
+    return name;
+  if (name.indexOf('border-') === 0 && /^border\-\w+\-\w+$/.test(name))
+    return name.match(/border\-\w+/)[0];
+  if (name.indexOf('border-') === 0 && /^border\-\w+$/.test(name))
+    return 'border';
+  if (name.indexOf('text-') === 0)
+    return name;
+
+  return name.replace(/^\-\w+\-/, '').match(/([a-zA-Z]+)/)[0].toLowerCase();
+}
+
+module.exports = extract;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/is-special.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/is-special.js
new file mode 100644
index 0000000..9631514
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/is-special.js
@@ -0,0 +1,5 @@
+function isSpecial(options, selector) {
+  return options.compatibility.selectors.special.test(selector);
+}
+
+module.exports = isSpecial;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-adjacent.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-adjacent.js
new file mode 100644
index 0000000..1485707
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-adjacent.js
@@ -0,0 +1,35 @@
+var optimizeProperties = require('../properties/optimizer');
+
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var cleanUpSelectors = require('./clean-up').selectors;
+var isSpecial = require('./is-special');
+
+function mergeAdjacent(tokens, options, validator) {
+  var lastToken = [null, [], []];
+  var adjacentSpace = options.compatibility.selectors.adjacentSpace;
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    if (token[0] != 'selector') {
+      lastToken = [null, [], []];
+      continue;
+    }
+
+    if (lastToken[0] == 'selector' && stringifySelectors(token[1]) == stringifySelectors(lastToken[1])) {
+      var joinAt = [lastToken[2].length];
+      Array.prototype.push.apply(lastToken[2], token[2]);
+      optimizeProperties(token[1], lastToken[2], joinAt, true, options, validator);
+      token[2] = [];
+    } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) &&
+        !isSpecial(options, stringifySelectors(token[1])) && !isSpecial(options, stringifySelectors(lastToken[1]))) {
+      lastToken[1] = cleanUpSelectors(lastToken[1].concat(token[1]), false, adjacentSpace);
+      token[2] = [];
+    } else {
+      lastToken = token;
+    }
+  }
+}
+
+module.exports = mergeAdjacent;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-media-queries.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-media-queries.js
new file mode 100644
index 0000000..0df0d6f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-media-queries.js
@@ -0,0 +1,64 @@
+var canReorder = require('./reorderable').canReorder;
+var extractProperties = require('./extractor');
+
+function mergeMediaQueries(tokens) {
+  var candidates = {};
+  var reduced = [];
+
+  for (var i = tokens.length - 1; i >= 0; i--) {
+    var token = tokens[i];
+    if (token[0] != 'block')
+      continue;
+
+    var candidate = candidates[token[1][0]];
+    if (!candidate) {
+      candidate = [];
+      candidates[token[1][0]] = candidate;
+    }
+
+    candidate.push(i);
+  }
+
+  for (var name in candidates) {
+    var positions = candidates[name];
+
+    positionLoop:
+    for (var j = positions.length - 1; j > 0; j--) {
+      var positionOne = positions[j];
+      var tokenOne = tokens[positionOne];
+      var positionTwo = positions[j - 1];
+      var tokenTwo = tokens[positionTwo];
+
+      directionLoop:
+      for (var direction = 1; direction >= -1; direction -= 2) {
+        var topToBottom = direction == 1;
+        var from = topToBottom ? positionOne + 1 : positionTwo - 1;
+        var to = topToBottom ? positionTwo : positionOne;
+        var delta = topToBottom ? 1 : -1;
+        var source = topToBottom ? tokenOne : tokenTwo;
+        var target = topToBottom ? tokenTwo : tokenOne;
+        var movedProperties = extractProperties(source);
+
+        while (from != to) {
+          var traversedProperties = extractProperties(tokens[from]);
+          from += delta;
+
+          if (!canReorder(movedProperties, traversedProperties))
+            continue directionLoop;
+        }
+
+        target[2] = topToBottom ?
+          source[2].concat(target[2]) :
+          target[2].concat(source[2]);
+        source[2] = [];
+
+        reduced.push(target);
+        continue positionLoop;
+      }
+    }
+  }
+
+  return reduced;
+}
+
+module.exports = mergeMediaQueries;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-non-adjacent-by-body.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-non-adjacent-by-body.js
new file mode 100644
index 0000000..b38b3f8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-non-adjacent-by-body.js
@@ -0,0 +1,58 @@
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var cleanUpSelectors = require('./clean-up').selectors;
+var isSpecial = require('./is-special');
+
+function unsafeSelector(value) {
+  return /\.|\*| :/.test(value);
+}
+
+function isBemElement(token) {
+  var asString = stringifySelectors(token[1]);
+  return asString.indexOf('__') > -1 || asString.indexOf('--') > -1;
+}
+
+function withoutModifier(selector) {
+  return selector.replace(/--[^ ,>\+~:]+/g, '');
+}
+
+function removeAnyUnsafeElements(left, candidates) {
+  var leftSelector = withoutModifier(stringifySelectors(left[1]));
+
+  for (var body in candidates) {
+    var right = candidates[body];
+    var rightSelector = withoutModifier(stringifySelectors(right[1]));
+
+    if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1)
+      delete candidates[body];
+  }
+}
+
+function mergeNonAdjacentByBody(tokens, options) {
+  var candidates = {};
+  var adjacentSpace = options.compatibility.selectors.adjacentSpace;
+
+  for (var i = tokens.length - 1; i >= 0; i--) {
+    var token = tokens[i];
+    if (token[0] != 'selector')
+      continue;
+
+    if (token[2].length > 0 && (!options.semanticMerging && unsafeSelector(stringifySelectors(token[1]))))
+      candidates = {};
+
+    if (token[2].length > 0 && options.semanticMerging && isBemElement(token))
+      removeAnyUnsafeElements(token, candidates);
+
+    var oldToken = candidates[stringifyBody(token[2])];
+    if (oldToken && !isSpecial(options, stringifySelectors(token[1])) && !isSpecial(options, stringifySelectors(oldToken[1]))) {
+      token[1] = cleanUpSelectors(oldToken[1].concat(token[1]), false, adjacentSpace);
+
+      oldToken[2] = [];
+      candidates[stringifyBody(token[2])] = null;
+    }
+
+    candidates[stringifyBody(token[2])] = token;
+  }
+}
+
+module.exports = mergeNonAdjacentByBody;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-non-adjacent-by-selector.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-non-adjacent-by-selector.js
new file mode 100644
index 0000000..28924b9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/merge-non-adjacent-by-selector.js
@@ -0,0 +1,76 @@
+var optimizeProperties = require('../properties/optimizer');
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var extractProperties = require('./extractor');
+var canReorder = require('./reorderable').canReorder;
+
+function mergeNonAdjacentBySelector(tokens, options, validator) {
+  var allSelectors = {};
+  var repeatedSelectors = [];
+  var i;
+
+  for (i = tokens.length - 1; i >= 0; i--) {
+    if (tokens[i][0] != 'selector')
+      continue;
+    if (tokens[i][2].length === 0)
+      continue;
+
+    var selector = stringifySelectors(tokens[i][1]);
+    allSelectors[selector] = [i].concat(allSelectors[selector] || []);
+
+    if (allSelectors[selector].length == 2)
+      repeatedSelectors.push(selector);
+  }
+
+  for (i = repeatedSelectors.length - 1; i >= 0; i--) {
+    var positions = allSelectors[repeatedSelectors[i]];
+
+    selectorIterator:
+    for (var j = positions.length - 1; j > 0; j--) {
+      var positionOne = positions[j - 1];
+      var tokenOne = tokens[positionOne];
+      var positionTwo = positions[j];
+      var tokenTwo = tokens[positionTwo];
+
+      directionIterator:
+      for (var direction = 1; direction >= -1; direction -= 2) {
+        var topToBottom = direction == 1;
+        var from = topToBottom ? positionOne + 1 : positionTwo - 1;
+        var to = topToBottom ? positionTwo : positionOne;
+        var delta = topToBottom ? 1 : -1;
+        var moved = topToBottom ? tokenOne : tokenTwo;
+        var target = topToBottom ? tokenTwo : tokenOne;
+        var movedProperties = extractProperties(moved);
+        var joinAt;
+
+        while (from != to) {
+          var traversedProperties = extractProperties(tokens[from]);
+          from += delta;
+
+          // traversed then moved as we move selectors towards the start
+          var reorderable = topToBottom ?
+            canReorder(movedProperties, traversedProperties) :
+            canReorder(traversedProperties, movedProperties);
+
+          if (!reorderable && !topToBottom)
+            continue selectorIterator;
+          if (!reorderable && topToBottom)
+            continue directionIterator;
+        }
+
+        if (topToBottom) {
+          joinAt = [moved[2].length];
+          Array.prototype.push.apply(moved[2], target[2]);
+          target[2] = moved[2];
+        } else {
+          joinAt = [target[2].length];
+          Array.prototype.push.apply(target[2], moved[2]);
+        }
+
+        optimizeProperties(target[1], target[2], joinAt, true, options, validator);
+        moved[2] = [];
+      }
+    }
+  }
+}
+
+module.exports = mergeNonAdjacentBySelector;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/reduce-non-adjacent.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/reduce-non-adjacent.js
new file mode 100644
index 0000000..1ad9c06
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/reduce-non-adjacent.js
@@ -0,0 +1,170 @@
+var optimizeProperties = require('../properties/optimizer');
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var isSpecial = require('./is-special');
+
+function reduceNonAdjacent(tokens, options, validator) {
+  var candidates = {};
+  var repeated = [];
+
+  for (var i = tokens.length - 1; i >= 0; i--) {
+    var token = tokens[i];
+
+    if (token[0] != 'selector')
+      continue;
+    if (token[2].length === 0)
+      continue;
+
+    var selectorAsString = stringifySelectors(token[1]);
+    var isComplexAndNotSpecial = token[1].length > 1 && !isSpecial(options, selectorAsString);
+    var wrappedSelectors = options.sourceMap ? wrappedSelectorsFrom(token[1]) : token[1];
+    var selectors = isComplexAndNotSpecial ?
+      [selectorAsString].concat(wrappedSelectors) :
+      [selectorAsString];
+
+    for (var j = 0, m = selectors.length; j < m; j++) {
+      var selector = selectors[j];
+
+      if (!candidates[selector])
+        candidates[selector] = [];
+      else
+        repeated.push(selector);
+
+      candidates[selector].push({
+        where: i,
+        list: wrappedSelectors,
+        isPartial: isComplexAndNotSpecial && j > 0,
+        isComplex: isComplexAndNotSpecial && j === 0
+      });
+    }
+  }
+
+  reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, validator);
+  reduceComplexNonAdjacentCases(tokens, candidates, options, validator);
+}
+
+function wrappedSelectorsFrom(list) {
+  var wrapped = [];
+
+  for (var i = 0; i < list.length; i++) {
+    wrapped.push([list[i][0]]);
+  }
+
+  return wrapped;
+}
+
+function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, validator) {
+  function filterOut(idx, bodies) {
+    return data[idx].isPartial && bodies.length === 0;
+  }
+
+  function reduceBody(token, newBody, processedCount, tokenIdx) {
+    if (!data[processedCount - tokenIdx - 1].isPartial)
+      token[2] = newBody;
+  }
+
+  for (var i = 0, l = repeated.length; i < l; i++) {
+    var selector = repeated[i];
+    var data = candidates[selector];
+
+    reduceSelector(tokens, selector, data, {
+      filterOut: filterOut,
+      callback: reduceBody
+    }, options, validator);
+  }
+}
+
+function reduceComplexNonAdjacentCases(tokens, candidates, options, validator) {
+  var localContext = {};
+
+  function filterOut(idx) {
+    return localContext.data[idx].where < localContext.intoPosition;
+  }
+
+  function collectReducedBodies(token, newBody, processedCount, tokenIdx) {
+    if (tokenIdx === 0)
+      localContext.reducedBodies.push(newBody);
+  }
+
+  allSelectors:
+  for (var complexSelector in candidates) {
+    var into = candidates[complexSelector];
+    if (!into[0].isComplex)
+      continue;
+
+    var intoPosition = into[into.length - 1].where;
+    var intoToken = tokens[intoPosition];
+    var reducedBodies = [];
+
+    var selectors = isSpecial(options, complexSelector) ?
+      [complexSelector] :
+      into[0].list;
+
+    localContext.intoPosition = intoPosition;
+    localContext.reducedBodies = reducedBodies;
+
+    for (var j = 0, m = selectors.length; j < m; j++) {
+      var selector = selectors[j];
+      var data = candidates[selector];
+
+      if (data.length < 2)
+        continue allSelectors;
+
+      localContext.data = data;
+
+      reduceSelector(tokens, selector, data, {
+        filterOut: filterOut,
+        callback: collectReducedBodies
+      }, options, validator);
+
+      if (stringifyBody(reducedBodies[reducedBodies.length - 1]) != stringifyBody(reducedBodies[0]))
+        continue allSelectors;
+    }
+
+    intoToken[2] = reducedBodies[0];
+  }
+}
+
+function reduceSelector(tokens, selector, data, context, options, validator) {
+  var bodies = [];
+  var bodiesAsList = [];
+  var joinsAt = [];
+  var processedTokens = [];
+
+  for (var j = data.length - 1, m = 0; j >= 0; j--) {
+    if (context.filterOut(j, bodies))
+      continue;
+
+    var where = data[j].where;
+    var token = tokens[where];
+
+    bodies = bodies.concat(token[2]);
+    bodiesAsList.push(token[2]);
+    processedTokens.push(where);
+  }
+
+  for (j = 0, m = bodiesAsList.length; j < m; j++) {
+    if (bodiesAsList[j].length > 0)
+      joinsAt.push((joinsAt[j - 1] || 0) + bodiesAsList[j].length);
+  }
+
+  optimizeProperties(selector, bodies, joinsAt, false, options, validator);
+
+  var processedCount = processedTokens.length;
+  var propertyIdx = bodies.length - 1;
+  var tokenIdx = processedCount - 1;
+
+  while (tokenIdx >= 0) {
+     if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) {
+      propertyIdx--;
+      continue;
+    }
+
+    var newBody = bodies.splice(propertyIdx + 1);
+    context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
+
+    tokenIdx--;
+  }
+}
+
+module.exports = reduceNonAdjacent;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/remove-duplicate-media-queries.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/remove-duplicate-media-queries.js
new file mode 100644
index 0000000..44070b0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/remove-duplicate-media-queries.js
@@ -0,0 +1,21 @@
+var stringifyAll = require('../stringifier/one-time').all;
+
+function removeDuplicateMediaQueries(tokens) {
+  var candidates = {};
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+    if (token[0] != 'block')
+      continue;
+
+    var key = token[1][0] + '%' + stringifyAll(token[2]);
+    var candidate = candidates[key];
+
+    if (candidate)
+      candidate[2] = [];
+
+    candidates[key] = token;
+  }
+}
+
+module.exports = removeDuplicateMediaQueries;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/remove-duplicates.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/remove-duplicates.js
new file mode 100644
index 0000000..3a2ce95
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/remove-duplicates.js
@@ -0,0 +1,41 @@
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+
+function removeDuplicates(tokens) {
+  var matched = {};
+  var moreThanOnce = [];
+  var id, token;
+  var body, bodies;
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    token = tokens[i];
+    if (token[0] != 'selector')
+      continue;
+
+    id = stringifySelectors(token[1]);
+
+    if (matched[id] && matched[id].length == 1)
+      moreThanOnce.push(id);
+    else
+      matched[id] = matched[id] || [];
+
+    matched[id].push(i);
+  }
+
+  for (i = 0, l = moreThanOnce.length; i < l; i++) {
+    id = moreThanOnce[i];
+    bodies = [];
+
+    for (var j = matched[id].length - 1; j >= 0; j--) {
+      token = tokens[matched[id][j]];
+      body = stringifyBody(token[2]);
+
+      if (bodies.indexOf(body) > -1)
+        token[2] = [];
+      else
+        bodies.push(body);
+    }
+  }
+}
+
+module.exports = removeDuplicates;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/reorderable.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/reorderable.js
new file mode 100644
index 0000000..2f44eb7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/reorderable.js
@@ -0,0 +1,79 @@
+// TODO: it'd be great to merge it with the other canReorder functionality
+
+var FLEX_PROPERTIES = /align\-items|box\-align|box\-pack|flex|justify/;
+var BORDER_PROPERTIES = /^border\-(top|right|bottom|left|color|style|width|radius)/;
+
+function canReorder(left, right) {
+  for (var i = right.length - 1; i >= 0; i--) {
+    for (var j = left.length - 1; j >= 0; j--) {
+      if (!canReorderSingle(left[j], right[i]))
+        return false;
+    }
+  }
+
+  return true;
+}
+
+function canReorderSingle(left, right) {
+  var leftName = left[0];
+  var leftValue = left[1];
+  var leftNameRoot = left[2];
+  var leftSelector = left[5];
+  var leftInSpecificSelector = left[6];
+  var rightName = right[0];
+  var rightValue = right[1];
+  var rightNameRoot = right[2];
+  var rightSelector = right[5];
+  var rightInSpecificSelector = right[6];
+
+  if (leftName == 'font' && rightName == 'line-height' || rightName == 'font' && leftName == 'line-height')
+    return false;
+  if (FLEX_PROPERTIES.test(leftName) && FLEX_PROPERTIES.test(rightName))
+    return false;
+  if (leftNameRoot == rightNameRoot && unprefixed(leftName) == unprefixed(rightName) && (vendorPrefixed(leftName) ^ vendorPrefixed(rightName)))
+    return false;
+  if (leftNameRoot == 'border' && BORDER_PROPERTIES.test(rightNameRoot) && (leftName == 'border' || leftName == rightNameRoot))
+    return false;
+  if (rightNameRoot == 'border' && BORDER_PROPERTIES.test(leftNameRoot) && (rightName == 'border' || rightName == leftNameRoot))
+    return false;
+  if (leftNameRoot != rightNameRoot)
+    return true;
+  if (leftName == rightName && leftNameRoot == rightNameRoot && (leftValue == rightValue || withDifferentVendorPrefix(leftValue, rightValue)))
+    return true;
+  if (leftName != rightName && leftNameRoot == rightNameRoot && leftName != leftNameRoot && rightName != rightNameRoot)
+    return true;
+  if (leftName != rightName && leftNameRoot == rightNameRoot && leftValue == rightValue)
+    return true;
+  if (rightInSpecificSelector && leftInSpecificSelector && selectorsDoNotOverlap(rightSelector, leftSelector))
+    return true;
+
+  return false;
+}
+
+function vendorPrefixed(name) {
+  return /^\-(?:moz|webkit|ms|o)\-/.test(name);
+}
+
+function unprefixed(name) {
+  return name.replace(/^\-(?:moz|webkit|ms|o)\-/, '');
+}
+
+function withDifferentVendorPrefix(value1, value2) {
+  return vendorPrefixed(value1) && vendorPrefixed(value2) && value1.split('-')[1] != value2.split('-')[2];
+}
+
+function selectorsDoNotOverlap(s1, s2) {
+  for (var i = 0, l = s1.length; i < l; i++) {
+    for (var j = 0, m = s2.length; j < m; j++) {
+      if (s1[i][0] == s2[j][0])
+        return false;
+    }
+  }
+
+  return true;
+}
+
+module.exports = {
+  canReorder: canReorder,
+  canReorderSingle: canReorderSingle
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/restructure.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/restructure.js
new file mode 100644
index 0000000..88cfc04
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/restructure.js
@@ -0,0 +1,352 @@
+var extractProperties = require('./extractor');
+var canReorderSingle = require('./reorderable').canReorderSingle;
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var cleanUpSelectorDuplicates = require('./clean-up').selectorDuplicates;
+var isSpecial = require('./is-special');
+
+function naturalSorter(a, b) {
+  return a > b;
+}
+
+function restructure(tokens, options) {
+  var movableTokens = {};
+  var movedProperties = [];
+  var multiPropertyMoveCache = {};
+  var movedToBeDropped = [];
+  var maxCombinationsLevel = 2;
+  var ID_JOIN_CHARACTER = '%';
+
+  function sendToMultiPropertyMoveCache(position, movedProperty, allFits) {
+    for (var i = allFits.length - 1; i >= 0; i--) {
+      var fit = allFits[i][0];
+      var id = addToCache(movedProperty, fit);
+
+      if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) {
+        removeAllMatchingFromCache(id);
+        break;
+      }
+    }
+  }
+
+  function addToCache(movedProperty, fit) {
+    var id = cacheId(fit);
+    multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || [];
+    multiPropertyMoveCache[id].push([movedProperty, fit]);
+    return id;
+  }
+
+  function removeAllMatchingFromCache(matchId) {
+    var matchSelectors = matchId.split(ID_JOIN_CHARACTER);
+    var forRemoval = [];
+    var i;
+
+    for (var id in multiPropertyMoveCache) {
+      var selectors = id.split(ID_JOIN_CHARACTER);
+      for (i = selectors.length - 1; i >= 0; i--) {
+        if (matchSelectors.indexOf(selectors[i]) > -1) {
+          forRemoval.push(id);
+          break;
+        }
+      }
+    }
+
+    for (i = forRemoval.length - 1; i >= 0; i--) {
+      delete multiPropertyMoveCache[forRemoval[i]];
+    }
+  }
+
+  function cacheId(cachedTokens) {
+    var id = [];
+    for (var i = 0, l = cachedTokens.length; i < l; i++) {
+      id.push(stringifySelectors(cachedTokens[i][1]));
+    }
+    return id.join(ID_JOIN_CHARACTER);
+  }
+
+  function tokensToMerge(sourceTokens) {
+    var uniqueTokensWithBody = [];
+    var mergeableTokens = [];
+
+    for (var i = sourceTokens.length - 1; i >= 0; i--) {
+      if (isSpecial(options, stringifySelectors(sourceTokens[i][1])))
+        continue;
+
+      mergeableTokens.unshift(sourceTokens[i]);
+      if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1)
+        uniqueTokensWithBody.push(sourceTokens[i]);
+    }
+
+    return uniqueTokensWithBody.length > 1 ?
+      mergeableTokens :
+      [];
+  }
+
+  function shortenIfPossible(position, movedProperty) {
+    var name = movedProperty[0];
+    var value = movedProperty[1];
+    var key = movedProperty[4];
+    var valueSize = name.length + value.length + 1;
+    var allSelectors = [];
+    var qualifiedTokens = [];
+
+    var mergeableTokens = tokensToMerge(movableTokens[key]);
+    if (mergeableTokens.length < 2)
+      return;
+
+    var allFits = findAllFits(mergeableTokens, valueSize, 1);
+    var bestFit = allFits[0];
+    if (bestFit[1] > 0)
+      return sendToMultiPropertyMoveCache(position, movedProperty, allFits);
+
+    for (var i = bestFit[0].length - 1; i >=0; i--) {
+      allSelectors = bestFit[0][i][1].concat(allSelectors);
+      qualifiedTokens.unshift(bestFit[0][i]);
+    }
+
+    allSelectors = cleanUpSelectorDuplicates(allSelectors);
+    dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens);
+  }
+
+  function fitSorter(fit1, fit2) {
+    return fit1[1] > fit2[1];
+  }
+
+  function findAllFits(mergeableTokens, propertySize, propertiesCount) {
+    var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1);
+    return combinations.sort(fitSorter);
+  }
+
+  function allCombinations(tokensVariant, propertySize, propertiesCount, level) {
+    var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]];
+    if (tokensVariant.length > 2 && level > 0) {
+      for (var i = tokensVariant.length - 1; i >= 0; i--) {
+        var subVariant = Array.prototype.slice.call(tokensVariant, 0);
+        subVariant.splice(i, 1);
+        differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1));
+      }
+    }
+
+    return differenceVariants;
+  }
+
+  function sizeDifference(tokensVariant, propertySize, propertiesCount) {
+    var allSelectorsSize = 0;
+    for (var i = tokensVariant.length - 1; i >= 0; i--) {
+      allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? stringifySelectors(tokensVariant[i][1]).length : -1;
+    }
+    return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
+  }
+
+  function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
+    var i, j, k, m;
+    var allProperties = [];
+
+    for (i = mergeableTokens.length - 1; i >= 0; i--) {
+      var mergeableToken = mergeableTokens[i];
+
+      for (j = mergeableToken[2].length - 1; j >= 0; j--) {
+        var mergeableProperty = mergeableToken[2][j];
+
+        for (k = 0, m = properties.length; k < m; k++) {
+          var property = properties[k];
+
+          var mergeablePropertyName = mergeableProperty[0][0];
+          var propertyName = property[0];
+          var propertyBody = property[4];
+          if (mergeablePropertyName == propertyName && stringifyBody([mergeableProperty]) == propertyBody) {
+            mergeableToken[2].splice(j, 1);
+            break;
+          }
+        }
+      }
+    }
+
+    for (i = properties.length - 1; i >= 0; i--) {
+      allProperties.unshift(properties[i][3]);
+    }
+
+    var newToken = ['selector', allSelectors, allProperties];
+    tokens.splice(position, 0, newToken);
+  }
+
+  function dropPropertiesAt(position, movedProperty) {
+    var key = movedProperty[4];
+    var toMove = movableTokens[key];
+
+    if (toMove && toMove.length > 1) {
+      if (!shortenMultiMovesIfPossible(position, movedProperty))
+        shortenIfPossible(position, movedProperty);
+    }
+  }
+
+  function shortenMultiMovesIfPossible(position, movedProperty) {
+    var candidates = [];
+    var propertiesAndMergableTokens = [];
+    var key = movedProperty[4];
+    var j, k;
+
+    var mergeableTokens = tokensToMerge(movableTokens[key]);
+    if (mergeableTokens.length < 2)
+      return;
+
+    movableLoop:
+    for (var value in movableTokens) {
+      var tokensList = movableTokens[value];
+
+      for (j = mergeableTokens.length - 1; j >= 0; j--) {
+        if (tokensList.indexOf(mergeableTokens[j]) == -1)
+          continue movableLoop;
+      }
+
+      candidates.push(value);
+    }
+
+    if (candidates.length < 2)
+      return false;
+
+    for (j = candidates.length - 1; j >= 0; j--) {
+      for (k = movedProperties.length - 1; k >= 0; k--) {
+        if (movedProperties[k][4] == candidates[j]) {
+          propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]);
+          break;
+        }
+      }
+    }
+
+    return processMultiPropertyMove(position, propertiesAndMergableTokens);
+  }
+
+  function processMultiPropertyMove(position, propertiesAndMergableTokens) {
+    var valueSize = 0;
+    var properties = [];
+    var property;
+
+    for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
+      property = propertiesAndMergableTokens[i][0];
+      var fullValue = property[4];
+      valueSize += fullValue.length + (i > 0 ? 1 : 0);
+
+      properties.push(property);
+    }
+
+    var mergeableTokens = propertiesAndMergableTokens[0][1];
+    var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0];
+    if (bestFit[1] > 0)
+      return false;
+
+    var allSelectors = [];
+    var qualifiedTokens = [];
+    for (i = bestFit[0].length - 1; i >= 0; i--) {
+      allSelectors = bestFit[0][i][1].concat(allSelectors);
+      qualifiedTokens.unshift(bestFit[0][i]);
+    }
+
+    allSelectors = cleanUpSelectorDuplicates(allSelectors);
+    dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens);
+
+    for (i = properties.length - 1; i >= 0; i--) {
+      property = properties[i];
+      var index = movedProperties.indexOf(property);
+
+      delete movableTokens[property[4]];
+
+      if (index > -1 && movedToBeDropped.indexOf(index) == -1)
+        movedToBeDropped.push(index);
+    }
+
+    return true;
+  }
+
+  function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) {
+    var propertyName = property[0];
+    var movedPropertyName = movedProperty[0];
+    if (propertyName != movedPropertyName)
+      return false;
+
+    var key = movedProperty[4];
+    var toMove = movableTokens[key];
+    return toMove && toMove.indexOf(token) > -1;
+  }
+
+  for (var i = tokens.length - 1; i >= 0; i--) {
+    var token = tokens[i];
+    var isSelector;
+    var j, k, m;
+
+    if (token[0] == 'selector') {
+      isSelector = true;
+    } else if (token[0] == 'block') {
+      isSelector = false;
+    } else {
+      continue;
+    }
+
+    // We cache movedProperties.length as it may change in the loop
+    var movedCount = movedProperties.length;
+
+    var properties = extractProperties(token);
+    movedToBeDropped = [];
+
+    var unmovableInCurrentToken = [];
+    for (j = properties.length - 1; j >= 0; j--) {
+      for (k = j - 1; k >= 0; k--) {
+        if (!canReorderSingle(properties[j], properties[k])) {
+          unmovableInCurrentToken.push(j);
+          break;
+        }
+      }
+    }
+
+    for (j = properties.length - 1; j >= 0; j--) {
+      var property = properties[j];
+      var movedSameProperty = false;
+
+      for (k = 0; k < movedCount; k++) {
+        var movedProperty = movedProperties[k];
+
+        if (movedToBeDropped.indexOf(k) == -1 && !canReorderSingle(property, movedProperty) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token)) {
+          dropPropertiesAt(i + 1, movedProperty, token);
+
+          if (movedToBeDropped.indexOf(k) == -1) {
+            movedToBeDropped.push(k);
+            delete movableTokens[movedProperty[4]];
+          }
+        }
+
+        if (!movedSameProperty)
+          movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1];
+      }
+
+      if (!isSelector || unmovableInCurrentToken.indexOf(j) > -1)
+        continue;
+
+      var key = property[4];
+      movableTokens[key] = movableTokens[key] || [];
+      movableTokens[key].push(token);
+
+      if (!movedSameProperty)
+        movedProperties.push(property);
+    }
+
+    movedToBeDropped = movedToBeDropped.sort(naturalSorter);
+    for (j = 0, m = movedToBeDropped.length; j < m; j++) {
+      var dropAt = movedToBeDropped[j] - j;
+      movedProperties.splice(dropAt, 1);
+    }
+  }
+
+  var position = tokens[0] && tokens[0][0] == 'at-rule' && tokens[0][1][0].indexOf('@charset') === 0 ? 1 : 0;
+  for (; position < tokens.length - 1; position++) {
+    var isImportRule = tokens[position][0] === 'at-rule' && tokens[position][1][0].indexOf('@import') === 0;
+    var isEscapedCommentSpecial = tokens[position][0] === 'text' && tokens[position][1][0].indexOf('__ESCAPED_COMMENT_SPECIAL') === 0;
+    if (!(isImportRule || isEscapedCommentSpecial))
+      break;
+  }
+
+  for (i = 0; i < movedProperties.length; i++) {
+    dropPropertiesAt(position, movedProperties[i]);
+  }
+}
+
+module.exports = restructure;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/simple.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/simple.js
new file mode 100644
index 0000000..f43f6d2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/selectors/simple.js
@@ -0,0 +1,444 @@
+var cleanUpSelectors = require('./clean-up').selectors;
+var cleanUpBlock = require('./clean-up').block;
+var cleanUpAtRule = require('./clean-up').atRule;
+var split = require('../utils/split');
+
+var RGB = require('../colors/rgb');
+var HSL = require('../colors/hsl');
+var HexNameShortener = require('../colors/hex-name-shortener');
+
+var wrapForOptimizing = require('../properties/wrap-for-optimizing').all;
+var restoreFromOptimizing = require('../properties/restore-from-optimizing');
+var removeUnused = require('../properties/remove-unused');
+
+var DEFAULT_ROUNDING_PRECISION = 2;
+var CHARSET_TOKEN = '@charset';
+var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i');
+
+var FONT_NUMERAL_WEIGHTS = ['100', '200', '300', '400', '500', '600', '700', '800', '900'];
+var FONT_NAME_WEIGHTS = ['normal', 'bold', 'bolder', 'lighter'];
+var FONT_NAME_WEIGHTS_WITHOUT_NORMAL = ['bold', 'bolder', 'lighter'];
+
+var WHOLE_PIXEL_VALUE = /(?:^|\s|\()(-?\d+)px/;
+var TIME_VALUE = /^(\-?[\d\.]+)(m?s)$/;
+
+var valueMinifiers = {
+  'background': function (value, index, total) {
+    return index === 0 && total == 1 && (value == 'none' || value == 'transparent') ? '0 0' : value;
+  },
+  'font-weight': function (value) {
+    if (value == 'normal')
+      return '400';
+    else if (value == 'bold')
+      return '700';
+    else
+      return value;
+  },
+  'outline': function (value, index, total) {
+    return index === 0 && total == 1 && value == 'none' ? '0' : value;
+  }
+};
+
+function isNegative(property, idx) {
+  return property.value[idx] && property.value[idx][0][0] == '-' && parseFloat(property.value[idx][0]) < 0;
+}
+
+function zeroMinifier(name, value) {
+  if (value.indexOf('0') == -1)
+    return value;
+
+  if (value.indexOf('-') > -1) {
+    value = value
+      .replace(/([^\w\d\-]|^)\-0([^\.]|$)/g, '$10$2')
+      .replace(/([^\w\d\-]|^)\-0([^\.]|$)/g, '$10$2');
+  }
+
+  return value
+    .replace(/(^|\s)0+([1-9])/g, '$1$2')
+    .replace(/(^|\D)\.0+(\D|$)/g, '$10$2')
+    .replace(/(^|\D)\.0+(\D|$)/g, '$10$2')
+    .replace(/\.([1-9]*)0+(\D|$)/g, function (match, nonZeroPart, suffix) {
+      return (nonZeroPart.length > 0 ? '.' : '') + nonZeroPart + suffix;
+    })
+    .replace(/(^|\D)0\.(\d)/g, '$1.$2');
+}
+
+function zeroDegMinifier(_, value) {
+  if (value.indexOf('0deg') == -1)
+    return value;
+
+  return value.replace(/\(0deg\)/g, '(0)');
+}
+
+function whitespaceMinifier(name, value) {
+  if (name.indexOf('filter') > -1 || value.indexOf(' ') == -1)
+    return value;
+
+  value = value.replace(/\s+/g, ' ');
+
+  if (value.indexOf('calc') > -1)
+    value = value.replace(/\) ?\/ ?/g, ')/ ');
+
+  return value
+    .replace(/\( /g, '(')
+    .replace(/ \)/g, ')')
+    .replace(/, /g, ',');
+}
+
+function precisionMinifier(_, value, precisionOptions) {
+  if (precisionOptions.value === -1 || value.indexOf('.') === -1)
+    return value;
+
+  return value
+    .replace(precisionOptions.regexp, function (match, number) {
+      return Math.round(parseFloat(number) * precisionOptions.multiplier) / precisionOptions.multiplier + 'px';
+    })
+    .replace(/(\d)\.($|\D)/g, '$1$2');
+}
+
+function unitMinifier(name, value, unitsRegexp) {
+  if (/^(?:\-moz\-calc|\-webkit\-calc|calc)\(/.test(value))
+    return value;
+
+  if (name == 'flex' || name == '-ms-flex' || name == '-webkit-flex' || name == 'flex-basis' || name == '-webkit-flex-basis')
+    return value;
+
+  if (value.indexOf('%') > 0 && (name == 'height' || name == 'max-height'))
+    return value;
+
+  return value
+    .replace(unitsRegexp, '$1' + '0' + '$2')
+    .replace(unitsRegexp, '$1' + '0' + '$2');
+}
+
+function multipleZerosMinifier(property) {
+  var values = property.value;
+  var spliceAt;
+
+  if (values.length == 4 && values[0][0] === '0' && values[1][0] === '0' && values[2][0] === '0' && values[3][0] === '0') {
+    if (property.name.indexOf('box-shadow') > -1)
+      spliceAt = 2;
+    else
+      spliceAt = 1;
+  }
+
+  if (spliceAt) {
+    property.value.splice(spliceAt);
+    property.dirty = true;
+  }
+}
+
+function colorMininifier(_, value, compatibility) {
+  if (value.indexOf('#') === -1 && value.indexOf('rgb') == -1 && value.indexOf('hsl') == -1)
+    return HexNameShortener.shorten(value);
+
+  value = value
+    .replace(/rgb\((\-?\d+),(\-?\d+),(\-?\d+)\)/g, function (match, red, green, blue) {
+      return new RGB(red, green, blue).toHex();
+    })
+    .replace(/hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/g, function (match, hue, saturation, lightness) {
+      return new HSL(hue, saturation, lightness).toHex();
+    })
+    .replace(/(^|[^='"])#([0-9a-f]{6})/gi, function (match, prefix, color) {
+      if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5])
+        return prefix + '#' + color[0] + color[2] + color[4];
+      else
+        return prefix + '#' + color;
+    })
+    .replace(/(rgb|rgba|hsl|hsla)\(([^\)]+)\)/g, function (match, colorFunction, colorDef) {
+      var tokens = colorDef.split(',');
+      var applies = (colorFunction == 'hsl' && tokens.length == 3) ||
+        (colorFunction == 'hsla' && tokens.length == 4) ||
+        (colorFunction == 'rgb' && tokens.length == 3 && colorDef.indexOf('%') > 0) ||
+        (colorFunction == 'rgba' && tokens.length == 4 && colorDef.indexOf('%') > 0);
+      if (!applies)
+        return match;
+
+      if (tokens[1].indexOf('%') == -1)
+        tokens[1] += '%';
+      if (tokens[2].indexOf('%') == -1)
+        tokens[2] += '%';
+      return colorFunction + '(' + tokens.join(',') + ')';
+    });
+
+  if (compatibility.colors.opacity) {
+    value = value.replace(/(?:rgba|hsla)\(0,0%?,0%?,0\)/g, function (match) {
+      if (split(value, ',').pop().indexOf('gradient(') > -1)
+        return match;
+
+      return 'transparent';
+    });
+  }
+
+  return HexNameShortener.shorten(value);
+}
+
+function pixelLengthMinifier(_, value, compatibility) {
+  if (!WHOLE_PIXEL_VALUE.test(value))
+    return value;
+
+  return value.replace(WHOLE_PIXEL_VALUE, function (match, val) {
+    var newValue;
+    var intVal = parseInt(val);
+
+    if (intVal === 0)
+      return match;
+
+    if (compatibility.units.pt && intVal * 3 % 4 === 0)
+      newValue = intVal * 3 / 4 + 'pt';
+
+    if (compatibility.units.pc && intVal % 16 === 0)
+      newValue = intVal / 16 + 'pc';
+
+    if (compatibility.units.in && intVal % 96 === 0)
+      newValue = intVal / 96 + 'in';
+
+    if (newValue)
+      newValue = match.substring(0, match.indexOf(val)) + newValue;
+
+    return newValue && newValue.length < match.length ? newValue : match;
+  });
+}
+
+function timeUnitMinifier(_, value) {
+  if (!TIME_VALUE.test(value))
+    return value;
+
+  return value.replace(TIME_VALUE, function (match, val, unit) {
+    var newValue;
+
+    if (unit == 'ms') {
+      newValue = parseInt(val) / 1000 + 's';
+    } else if (unit == 's') {
+      newValue = parseFloat(val) * 1000 + 'ms';
+    }
+
+    return newValue.length < match.length ? newValue : match;
+  });
+}
+
+function minifyBorderRadius(property) {
+  var values = property.value;
+  var spliceAt;
+
+  if (values.length == 3 && values[1][0] == '/' && values[0][0] == values[2][0])
+    spliceAt = 1;
+  else if (values.length == 5 && values[2][0] == '/' && values[0][0] == values[3][0] && values[1][0] == values[4][0])
+    spliceAt = 2;
+  else if (values.length == 7 && values[3][0] == '/' && values[0][0] == values[4][0] && values[1][0] == values[5][0] && values[2][0] == values[6][0])
+    spliceAt = 3;
+  else if (values.length == 9 && values[4][0] == '/' && values[0][0] == values[5][0] && values[1][0] == values[6][0] && values[2][0] == values[7][0] && values[3][0] == values[8][0])
+    spliceAt = 4;
+
+  if (spliceAt) {
+    property.value.splice(spliceAt);
+    property.dirty = true;
+  }
+}
+
+function minifyFilter(property) {
+  if (property.value.length == 1) {
+    property.value[0][0] = property.value[0][0].replace(/progid:DXImageTransform\.Microsoft\.(Alpha|Chroma)(\W)/, function (match, filter, suffix) {
+      return filter.toLowerCase() + suffix;
+    });
+  }
+
+  property.value[0][0] = property.value[0][0]
+    .replace(/,(\S)/g, ', $1')
+    .replace(/ ?= ?/g, '=');
+}
+
+function minifyFont(property) {
+  var values = property.value;
+  var hasNumeral = FONT_NUMERAL_WEIGHTS.indexOf(values[0][0]) > -1 ||
+    values[1] && FONT_NUMERAL_WEIGHTS.indexOf(values[1][0]) > -1 ||
+    values[2] && FONT_NUMERAL_WEIGHTS.indexOf(values[2][0]) > -1;
+
+  if (hasNumeral)
+    return;
+
+  if (values[1] == '/')
+    return;
+
+  var normalCount = 0;
+  if (values[0][0] == 'normal')
+    normalCount++;
+  if (values[1] && values[1][0] == 'normal')
+    normalCount++;
+  if (values[2] && values[2][0] == 'normal')
+    normalCount++;
+
+  if (normalCount > 1)
+    return;
+
+  var toOptimize;
+  if (FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[0][0]) > -1)
+    toOptimize = 0;
+  else if (values[1] && FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[1][0]) > -1)
+    toOptimize = 1;
+  else if (values[2] && FONT_NAME_WEIGHTS_WITHOUT_NORMAL.indexOf(values[2][0]) > -1)
+    toOptimize = 2;
+  else if (FONT_NAME_WEIGHTS.indexOf(values[0][0]) > -1)
+    toOptimize = 0;
+  else if (values[1] && FONT_NAME_WEIGHTS.indexOf(values[1][0]) > -1)
+    toOptimize = 1;
+  else if (values[2] && FONT_NAME_WEIGHTS.indexOf(values[2][0]) > -1)
+    toOptimize = 2;
+
+  if (toOptimize !== undefined) {
+    property.value[toOptimize][0] = valueMinifiers['font-weight'](values[toOptimize][0]);
+    property.dirty = true;
+  }
+}
+
+function optimizeBody(properties, options) {
+  var property, name, value;
+  var _properties = wrapForOptimizing(properties);
+
+  for (var i = 0, l = _properties.length; i < l; i++) {
+    property = _properties[i];
+    name = property.name;
+
+    if (property.hack && (
+        (property.hack == 'star' || property.hack == 'underscore') && !options.compatibility.properties.iePrefixHack ||
+        property.hack == 'backslash' && !options.compatibility.properties.ieSuffixHack ||
+        property.hack == 'bang' && !options.compatibility.properties.ieBangHack))
+      property.unused = true;
+
+    if (name.indexOf('padding') === 0 && (isNegative(property, 0) || isNegative(property, 1) || isNegative(property, 2) || isNegative(property, 3)))
+      property.unused = true;
+
+    if (property.unused)
+      continue;
+
+    if (property.variable) {
+      if (property.block)
+        optimizeBody(property.value[0], options);
+      continue;
+    }
+
+    for (var j = 0, m = property.value.length; j < m; j++) {
+      value = property.value[j][0];
+
+      if (valueMinifiers[name])
+        value = valueMinifiers[name](value, j, m);
+
+      value = whitespaceMinifier(name, value);
+      value = precisionMinifier(name, value, options.precision);
+      value = pixelLengthMinifier(name, value, options.compatibility);
+      value = timeUnitMinifier(name, value);
+      value = zeroMinifier(name, value);
+      if (options.compatibility.properties.zeroUnits) {
+        value = zeroDegMinifier(name, value);
+        value = unitMinifier(name, value, options.unitsRegexp);
+      }
+      if (options.compatibility.properties.colors)
+        value = colorMininifier(name, value, options.compatibility);
+
+      property.value[j][0] = value;
+    }
+
+    multipleZerosMinifier(property);
+
+    if (name.indexOf('border') === 0 && name.indexOf('radius') > 0)
+      minifyBorderRadius(property);
+    else if (name == 'filter')
+      minifyFilter(property);
+    else if (name == 'font')
+      minifyFont(property);
+  }
+
+  restoreFromOptimizing(_properties, true);
+  removeUnused(_properties);
+}
+
+function cleanupCharsets(tokens) {
+  var hasCharset = false;
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    if (token[0] != 'at-rule')
+      continue;
+
+    if (!CHARSET_REGEXP.test(token[1][0]))
+      continue;
+
+    if (hasCharset || token[1][0].indexOf(CHARSET_TOKEN) == -1) {
+      tokens.splice(i, 1);
+      i--;
+      l--;
+    } else {
+      hasCharset = true;
+      tokens.splice(i, 1);
+      tokens.unshift(['at-rule', [token[1][0].replace(CHARSET_REGEXP, CHARSET_TOKEN)]]);
+    }
+  }
+}
+
+function buildUnitRegexp(options) {
+  var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%'];
+  var otherUnits = ['ch', 'rem', 'vh', 'vm', 'vmax', 'vmin', 'vw'];
+
+  otherUnits.forEach(function (unit) {
+    if (options.compatibility.units[unit])
+      units.push(unit);
+  });
+
+  return new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')(\\W|$)', 'g');
+}
+
+function buildPrecision(options) {
+  var precision = {};
+
+  precision.value = options.roundingPrecision === undefined ?
+    DEFAULT_ROUNDING_PRECISION :
+    options.roundingPrecision;
+  precision.multiplier = Math.pow(10, precision.value);
+  precision.regexp = new RegExp('(\\d*\\.\\d{' + (precision.value + 1) + ',})px', 'g');
+
+  return precision;
+}
+
+function optimize(tokens, options) {
+  var ie7Hack = options.compatibility.selectors.ie7Hack;
+  var adjacentSpace = options.compatibility.selectors.adjacentSpace;
+  var spaceAfterClosingBrace = options.compatibility.properties.spaceAfterClosingBrace;
+  var mayHaveCharset = false;
+
+  options.unitsRegexp = buildUnitRegexp(options);
+  options.precision = buildPrecision(options);
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    switch (token[0]) {
+      case 'selector':
+        token[1] = cleanUpSelectors(token[1], !ie7Hack, adjacentSpace);
+        optimizeBody(token[2], options);
+        break;
+      case 'block':
+        cleanUpBlock(token[1], spaceAfterClosingBrace);
+        optimize(token[2], options);
+        break;
+      case 'flat-block':
+        cleanUpBlock(token[1], spaceAfterClosingBrace);
+        optimizeBody(token[2], options);
+        break;
+      case 'at-rule':
+        cleanUpAtRule(token[1]);
+        mayHaveCharset = true;
+    }
+
+    if (token[1].length === 0 || (token[2] && token[2].length === 0)) {
+      tokens.splice(i, 1);
+      i--;
+      l--;
+    }
+  }
+
+  if (mayHaveCharset)
+    cleanupCharsets(tokens);
+}
+
+module.exports = optimize;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/source-maps/track.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/source-maps/track.js
new file mode 100644
index 0000000..735a58d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/source-maps/track.js
@@ -0,0 +1,119 @@
+var escapePrefix = '__ESCAPED_';
+
+function trackPrefix(value, context, interestingContent) {
+  if (!interestingContent && value.indexOf('\n') == -1) {
+    if (value.indexOf(escapePrefix) === 0) {
+      return value;
+    } else {
+      context.column += value.length;
+      return;
+    }
+  }
+
+  var withoutContent = 0;
+  var split = value.split('\n');
+  var total = split.length;
+  var shift = 0;
+
+  while (true) {
+    if (withoutContent == total - 1)
+      break;
+
+    var part = split[withoutContent];
+    if (/\S/.test(part))
+      break;
+
+    shift += part.length + 1;
+    withoutContent++;
+  }
+
+  context.line += withoutContent;
+  context.column = withoutContent > 0 ? 0 : context.column;
+  context.column += /^(\s)*/.exec(split[withoutContent])[0].length;
+
+  return value.substring(shift).trimLeft();
+}
+
+function sourceFor(originalMetadata, contextMetadata, context) {
+  var source = originalMetadata.source || contextMetadata.source;
+
+  if (source && context.resolvePath)
+    return context.resolvePath(contextMetadata.source, source);
+
+  return source;
+}
+
+function snapshot(data, context, fallbacks) {
+  var metadata = {
+    line: context.line,
+    column: context.column,
+    source: context.source
+  };
+  var sourceContent = null;
+  var sourceMetadata = context.sourceMapTracker.isTracking(metadata.source) ?
+    context.sourceMapTracker.originalPositionFor(metadata, data, fallbacks || 0) :
+    {};
+
+  metadata.line = sourceMetadata.line || metadata.line;
+  metadata.column = sourceMetadata.column || metadata.column;
+  metadata.source = sourceMetadata.sourceResolved ?
+    sourceMetadata.source :
+    sourceFor(sourceMetadata, metadata, context);
+
+  if (context.sourceMapInlineSources) {
+    var sourceMapSourcesContent = context.sourceMapTracker.sourcesContentFor(context.source);
+    sourceContent = sourceMapSourcesContent && sourceMapSourcesContent[metadata.source] ?
+      sourceMapSourcesContent :
+      context.sourceReader.sourceAt(context.source);
+  }
+
+  return sourceContent ?
+    [metadata.line, metadata.column, metadata.source, sourceContent] :
+    [metadata.line, metadata.column, metadata.source];
+}
+
+function trackSuffix(data, context) {
+  var parts = data.split('\n');
+
+  for (var i = 0, l = parts.length; i < l; i++) {
+    var part = parts[i];
+    var cursor = 0;
+
+    if (i > 0) {
+      context.line++;
+      context.column = 0;
+    }
+
+    while (true) {
+      var next = part.indexOf(escapePrefix, cursor);
+
+      if (next == -1) {
+        context.column += part.substring(cursor).length;
+        break;
+      }
+
+      context.column += next - cursor;
+      cursor += next - cursor;
+
+      var escaped = part.substring(next, part.indexOf('__', next + 1) + 2);
+      var encodedValues = escaped.substring(escaped.indexOf('(') + 1, escaped.indexOf(')')).split(',');
+      context.line += ~~encodedValues[0];
+      context.column = (~~encodedValues[0] === 0 ? context.column : 0) + ~~encodedValues[1];
+      cursor += escaped.length;
+    }
+  }
+}
+
+function track(data, context, snapshotMetadata, fallbacks) {
+  var untracked = trackPrefix(data, context, snapshotMetadata);
+  var metadata = snapshotMetadata ?
+    snapshot(untracked, context, fallbacks) :
+    [];
+
+  if (untracked)
+    trackSuffix(untracked, context);
+
+  return metadata;
+}
+
+module.exports = track;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/helpers.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/helpers.js
new file mode 100644
index 0000000..32b331e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/helpers.js
@@ -0,0 +1,161 @@
+var lineBreak = require('os').EOL;
+
+var AT_RULE = 'at-rule';
+var PROPERTY_SEPARATOR = ';';
+
+function hasMoreProperties(tokens, index) {
+  for (var i = index, l = tokens.length; i < l; i++) {
+    if (typeof tokens[i] != 'string')
+      return true;
+  }
+
+  return false;
+}
+
+function supportsAfterClosingBrace(token) {
+  return token[0][0] == 'background' || token[0][0] == 'transform' || token[0][0] == 'src';
+}
+
+function isVariable(token, valueIndex) {
+  return token[valueIndex][0].indexOf('var(') === 0;
+}
+
+function afterClosingBrace(token, valueIndex) {
+  return token[valueIndex][0][token[valueIndex][0].length - 1] == ')' || token[valueIndex][0].indexOf('__ESCAPED_URL_CLEAN_CSS') === 0;
+}
+
+function afterComma(token, valueIndex) {
+  return token[valueIndex][0] == ',';
+}
+
+function afterSlash(token, valueIndex) {
+  return token[valueIndex][0] == '/';
+}
+
+function beforeComma(token, valueIndex) {
+  return token[valueIndex + 1] && token[valueIndex + 1][0] == ',';
+}
+
+function beforeSlash(token, valueIndex) {
+  return token[valueIndex + 1] && token[valueIndex + 1][0] == '/';
+}
+
+function inFilter(token) {
+  return token[0][0] == 'filter' || token[0][0] == '-ms-filter';
+}
+
+function inSpecialContext(token, valueIndex, context) {
+  return (!context.spaceAfterClosingBrace && supportsAfterClosingBrace(token) || isVariable(token, valueIndex)) && afterClosingBrace(token, valueIndex) ||
+    beforeSlash(token, valueIndex) ||
+    afterSlash(token, valueIndex) ||
+    beforeComma(token, valueIndex) ||
+    afterComma(token, valueIndex);
+}
+
+function selectors(tokens, context) {
+  var store = context.store;
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    store(tokens[i], context);
+
+    if (i < l - 1)
+      store(',', context);
+  }
+}
+
+function body(tokens, context) {
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    property(tokens, i, i == l - 1, context);
+  }
+}
+
+function property(tokens, position, isLast, context) {
+  var store = context.store;
+  var token = tokens[position];
+
+  if (typeof token == 'string') {
+    store(token, context);
+  } else if (token[0] == AT_RULE) {
+    propertyAtRule(token[1], isLast, context);
+  } else {
+    store(token[0], context);
+    store(':', context);
+    value(tokens, position, isLast, context);
+  }
+}
+
+function propertyAtRule(value, isLast, context) {
+  var store = context.store;
+
+  store(value, context);
+  if (!isLast)
+    store(PROPERTY_SEPARATOR, context);
+}
+
+function value(tokens, position, isLast, context) {
+  var store = context.store;
+  var token = tokens[position];
+  var isVariableDeclaration = token[0][0].indexOf('--') === 0;
+
+  if (isVariableDeclaration && Array.isArray(token[1][0][0])) {
+    store('{', context);
+    body(token[1], context);
+    store('};', context);
+    return;
+  }
+
+  for (var j = 1, m = token.length; j < m; j++) {
+    store(token[j], context);
+
+    if (j < m - 1 && (inFilter(token) || !inSpecialContext(token, j, context))) {
+      store(' ', context);
+    } else if (j == m - 1 && !isLast && hasMoreProperties(tokens, position + 1)) {
+      store(PROPERTY_SEPARATOR, context);
+    }
+  }
+}
+
+function all(tokens, context) {
+  var joinCharacter = context.keepBreaks ? lineBreak : '';
+  var store = context.store;
+
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    var token = tokens[i];
+
+    switch (token[0]) {
+      case 'at-rule':
+      case 'text':
+        store(token[1][0], context);
+        store(joinCharacter, context);
+        break;
+      case 'block':
+        selectors([token[1]], context);
+        store('{', context);
+        all(token[2], context);
+        store('}', context);
+        store(joinCharacter, context);
+        break;
+      case 'flat-block':
+        selectors([token[1]], context);
+        store('{', context);
+        body(token[2], context);
+        store('}', context);
+        store(joinCharacter, context);
+        break;
+      default:
+        selectors(token[1], context);
+        store('{', context);
+        body(token[2], context);
+        store('}', context);
+        store(joinCharacter, context);
+    }
+  }
+}
+
+module.exports = {
+  all: all,
+  body: body,
+  property: property,
+  selectors: selectors,
+  value: value
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/one-time.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/one-time.js
new file mode 100644
index 0000000..4a65c7f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/one-time.js
@@ -0,0 +1,50 @@
+var helpers = require('./helpers');
+
+function store(token, context) {
+  context.output.push(typeof token == 'string' ? token : token[0]);
+}
+
+function context() {
+  return {
+    output: [],
+    store: store
+  };
+}
+
+function all(tokens) {
+  var fakeContext = context();
+  helpers.all(tokens, fakeContext);
+  return fakeContext.output.join('');
+}
+
+function body(tokens) {
+  var fakeContext = context();
+  helpers.body(tokens, fakeContext);
+  return fakeContext.output.join('');
+}
+
+function property(tokens, position) {
+  var fakeContext = context();
+  helpers.property(tokens, position, true, fakeContext);
+  return fakeContext.output.join('');
+}
+
+function selectors(tokens) {
+  var fakeContext = context();
+  helpers.selectors(tokens, fakeContext);
+  return fakeContext.output.join('');
+}
+
+function value(tokens, position) {
+  var fakeContext = context();
+  helpers.value(tokens, position, true, fakeContext);
+  return fakeContext.output.join('');
+}
+
+module.exports = {
+  all: all,
+  body: body,
+  property: property,
+  selectors: selectors,
+  value: value
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/simple.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/simple.js
new file mode 100644
index 0000000..0673996
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/simple.js
@@ -0,0 +1,22 @@
+var all = require('./helpers').all;
+
+function store(token, context) {
+  context.output.push(typeof token == 'string' ? token : token[0]);
+}
+
+function stringify(tokens, options, restoreCallback) {
+  var context = {
+    keepBreaks: options.keepBreaks,
+    output: [],
+    spaceAfterClosingBrace: options.compatibility.properties.spaceAfterClosingBrace,
+    store: store
+  };
+
+  all(tokens, context, false);
+
+  return {
+    styles: restoreCallback(context.output.join('')).trim()
+  };
+}
+
+module.exports = stringify;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/source-maps.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/source-maps.js
new file mode 100644
index 0000000..7748614
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/stringifier/source-maps.js
@@ -0,0 +1,96 @@
+var SourceMapGenerator = require('source-map').SourceMapGenerator;
+var all = require('./helpers').all;
+
+var isWindows = process.platform == 'win32';
+var unknownSource = '$stdin';
+
+function store(element, context) {
+  var fromString = typeof element == 'string';
+  var value = fromString ? element : element[0];
+
+  if (value.indexOf('_') > -1)
+    value = context.restore(value, prefixContentFrom(context.output));
+
+  track(value, fromString ? null : element, context);
+  context.output.push(value);
+}
+
+function prefixContentFrom(values) {
+  var content = [];
+
+  for (var i = values.length - 1; i >= 0; i--) {
+    var value = values[i];
+    content.unshift(value);
+
+    if (value == '{' || value == ';')
+      break;
+  }
+
+  return content.join('');
+}
+
+function track(value, element, context) {
+  if (element)
+    trackAllMappings(element, context);
+
+  var parts = value.split('\n');
+  context.line += parts.length - 1;
+  context.column = parts.length > 1 ? 0 : (context.column + parts.pop().length);
+}
+
+function trackAllMappings(element, context) {
+  var mapping = element[element.length - 1];
+
+  if (!Array.isArray(mapping))
+    return;
+
+  for (var i = 0, l = mapping.length; i < l; i++) {
+    trackMapping(mapping[i], context);
+  }
+}
+
+function trackMapping(mapping, context) {
+  var source = mapping[2] || unknownSource;
+
+  if (isWindows)
+    source = source.replace(/\\/g, '/');
+
+  context.outputMap.addMapping({
+    generated: {
+      line: context.line,
+      column: context.column
+    },
+    source: source,
+    original: {
+      line: mapping[0],
+      column: mapping[1]
+    }
+  });
+
+  if (mapping[3])
+    context.outputMap.setSourceContent(source, mapping[3][mapping[2]]);
+}
+
+function stringify(tokens, options, restoreCallback, inputMapTracker) {
+  var context = {
+    column: 0,
+    inputMapTracker: inputMapTracker,
+    keepBreaks: options.keepBreaks,
+    line: 1,
+    output: [],
+    outputMap: new SourceMapGenerator(),
+    restore: restoreCallback,
+    sourceMapInlineSources: options.sourceMapInlineSources,
+    spaceAfterClosingBrace: options.compatibility.properties.spaceAfterClosingBrace,
+    store: store
+  };
+
+  all(tokens, context, false);
+
+  return {
+    sourceMap: context.outputMap,
+    styles: context.output.join('').trim()
+  };
+}
+
+module.exports = stringify;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/comments-processor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/comments-processor.js
new file mode 100644
index 0000000..941fc67
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/comments-processor.js
@@ -0,0 +1,131 @@
+var EscapeStore = require('./escape-store');
+var QuoteScanner = require('../utils/quote-scanner');
+
+var SPECIAL_COMMENT_PREFIX = '/*!';
+var COMMENT_PREFIX = '/*';
+var COMMENT_SUFFIX = '*/';
+
+var lineBreak = require('os').EOL;
+
+function CommentsProcessor(context, keepSpecialComments, keepBreaks, saveWaypoints) {
+  this.comments = new EscapeStore('COMMENT');
+  this.specialComments = new EscapeStore('COMMENT_SPECIAL');
+
+  this.context = context;
+  this.restored = 0;
+  this.keepAll = keepSpecialComments == '*';
+  this.keepOne = keepSpecialComments == '1' || keepSpecialComments === 1;
+  this.keepBreaks = keepBreaks;
+  this.saveWaypoints = saveWaypoints;
+}
+
+function quoteScannerFor(data) {
+  var quoteMap = [];
+  new QuoteScanner(data).each(function (quotedString, _, startsAt) {
+    quoteMap.push([startsAt, startsAt + quotedString.length]);
+  });
+
+  return function (position) {
+    for (var i = 0, l = quoteMap.length; i < l; i++) {
+      if (quoteMap[i][0] < position && quoteMap[i][1] > position)
+        return true;
+    }
+
+    return false;
+  };
+}
+
+CommentsProcessor.prototype.escape = function (data) {
+  var tempData = [];
+  var nextStart = 0;
+  var nextEnd = 0;
+  var cursor = 0;
+  var indent = 0;
+  var breaksCount;
+  var lastBreakAt;
+  var newIndent;
+  var isQuotedAt = quoteScannerFor(data);
+  var saveWaypoints = this.saveWaypoints;
+
+  for (; nextEnd < data.length;) {
+    nextStart = data.indexOf(COMMENT_PREFIX, cursor);
+    if (nextStart == -1)
+      break;
+
+    if (isQuotedAt(nextStart)) {
+      tempData.push(data.substring(cursor, nextStart + COMMENT_PREFIX.length));
+      cursor = nextStart + COMMENT_PREFIX.length;
+      continue;
+    }
+
+    nextEnd = data.indexOf(COMMENT_SUFFIX, nextStart + COMMENT_PREFIX.length);
+    if (nextEnd == -1) {
+      this.context.warnings.push('Broken comment: \'' + data.substring(nextStart) + '\'.');
+      nextEnd = data.length - 2;
+    }
+
+    tempData.push(data.substring(cursor, nextStart));
+
+    var comment = data.substring(nextStart, nextEnd + COMMENT_SUFFIX.length);
+    var isSpecialComment = comment.indexOf(SPECIAL_COMMENT_PREFIX) === 0;
+
+    if (saveWaypoints) {
+      breaksCount = comment.split(lineBreak).length - 1;
+      lastBreakAt = comment.lastIndexOf(lineBreak);
+      newIndent = lastBreakAt > 0 ?
+        comment.substring(lastBreakAt + lineBreak.length).length :
+        indent + comment.length;
+    }
+
+    if (saveWaypoints || isSpecialComment) {
+      var metadata = saveWaypoints ? [breaksCount, newIndent] : null;
+      var placeholder = isSpecialComment ?
+        this.specialComments.store(comment, metadata) :
+        this.comments.store(comment, metadata);
+      tempData.push(placeholder);
+    }
+
+    if (saveWaypoints)
+      indent = newIndent + 1;
+    cursor = nextEnd + COMMENT_SUFFIX.length;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+};
+
+function restore(context, data, from, isSpecial) {
+  var tempData = [];
+  var cursor = 0;
+
+  for (; cursor < data.length;) {
+    var nextMatch = from.nextMatch(data, cursor);
+    if (nextMatch.start < 0)
+      break;
+
+    tempData.push(data.substring(cursor, nextMatch.start));
+    var comment = from.restore(nextMatch.match);
+
+    if (isSpecial && (context.keepAll || (context.keepOne && context.restored === 0))) {
+      context.restored++;
+      tempData.push(comment);
+
+      cursor = nextMatch.end;
+    } else {
+      cursor = nextMatch.end + (context.keepBreaks && data[nextMatch.end] == lineBreak ? 1 : 0);
+    }
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+}
+
+CommentsProcessor.prototype.restore = function (data) {
+  data = restore(this, data, this.comments, false);
+  data = restore(this, data, this.specialComments, true);
+  return data;
+};
+
+module.exports = CommentsProcessor;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/escape-store.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/escape-store.js
new file mode 100644
index 0000000..8598d73
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/escape-store.js
@@ -0,0 +1,53 @@
+var placeholderBrace = '__';
+
+function EscapeStore(placeholderRoot) {
+  this.placeholderRoot = 'ESCAPED_' + placeholderRoot + '_CLEAN_CSS';
+  this.placeholderToData = {};
+  this.dataToPlaceholder = {};
+  this.count = 0;
+  this.restoreMatcher = new RegExp(this.placeholderRoot + '(\\d+)');
+}
+
+EscapeStore.prototype._nextPlaceholder = function (metadata) {
+  return {
+    index: this.count,
+    value: placeholderBrace + this.placeholderRoot + this.count++ + metadata + placeholderBrace
+  };
+};
+
+EscapeStore.prototype.store = function (data, metadata) {
+  var encodedMetadata = metadata ?
+    '(' + metadata.join(',') + ')' :
+    '';
+  var placeholder = this.dataToPlaceholder[data];
+
+  if (!placeholder) {
+    var nextPlaceholder = this._nextPlaceholder(encodedMetadata);
+    placeholder = nextPlaceholder.value;
+    this.placeholderToData[nextPlaceholder.index] = data;
+    this.dataToPlaceholder[data] = nextPlaceholder.value;
+  }
+
+  if (metadata)
+    placeholder = placeholder.replace(/\([^\)]+\)/, encodedMetadata);
+
+  return placeholder;
+};
+
+EscapeStore.prototype.nextMatch = function (data, cursor) {
+  var next = {};
+
+  next.start = data.indexOf(this.placeholderRoot, cursor) - placeholderBrace.length;
+  next.end = data.indexOf(placeholderBrace, next.start + placeholderBrace.length) + placeholderBrace.length;
+  if (next.start > -1 && next.end > -1)
+    next.match = data.substring(next.start, next.end);
+
+  return next;
+};
+
+EscapeStore.prototype.restore = function (placeholder) {
+  var index = this.restoreMatcher.exec(placeholder)[1];
+  return this.placeholderToData[index];
+};
+
+module.exports = EscapeStore;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/expressions-processor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/expressions-processor.js
new file mode 100644
index 0000000..3e22ced
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/expressions-processor.js
@@ -0,0 +1,117 @@
+var EscapeStore = require('./escape-store');
+
+var EXPRESSION_NAME = 'expression';
+var EXPRESSION_START = '(';
+var EXPRESSION_END = ')';
+var EXPRESSION_PREFIX = EXPRESSION_NAME + EXPRESSION_START;
+var BODY_START = '{';
+var BODY_END = '}';
+
+var lineBreak = require('os').EOL;
+
+function findEnd(data, start) {
+  var end = start + EXPRESSION_NAME.length;
+  var level = 0;
+  var quoted = false;
+  var braced = false;
+
+  while (true) {
+    var current = data[end++];
+
+    if (quoted) {
+      quoted = current != '\'' && current != '"';
+    } else {
+      quoted = current == '\'' || current == '"';
+
+      if (current == EXPRESSION_START)
+        level++;
+      if (current == EXPRESSION_END)
+        level--;
+      if (current == BODY_START)
+        braced = true;
+      if (current == BODY_END && !braced && level == 1) {
+        end--;
+        level--;
+      }
+    }
+
+    if (level === 0 && current == EXPRESSION_END)
+      break;
+    if (!current) {
+      end = data.substring(0, end).lastIndexOf(BODY_END);
+      break;
+    }
+  }
+
+  return end;
+}
+
+function ExpressionsProcessor(saveWaypoints) {
+  this.expressions = new EscapeStore('EXPRESSION');
+  this.saveWaypoints = saveWaypoints;
+}
+
+ExpressionsProcessor.prototype.escape = function (data) {
+  var nextStart = 0;
+  var nextEnd = 0;
+  var cursor = 0;
+  var tempData = [];
+  var indent = 0;
+  var breaksCount;
+  var lastBreakAt;
+  var newIndent;
+  var saveWaypoints = this.saveWaypoints;
+
+  for (; nextEnd < data.length;) {
+    nextStart = data.indexOf(EXPRESSION_PREFIX, nextEnd);
+    if (nextStart == -1)
+      break;
+
+    nextEnd = findEnd(data, nextStart);
+
+    var expression = data.substring(nextStart, nextEnd);
+    if (saveWaypoints) {
+      breaksCount = expression.split(lineBreak).length - 1;
+      lastBreakAt = expression.lastIndexOf(lineBreak);
+      newIndent = lastBreakAt > 0 ?
+        expression.substring(lastBreakAt + lineBreak.length).length :
+        indent + expression.length;
+    }
+
+    var metadata = saveWaypoints ? [breaksCount, newIndent] : null;
+    var placeholder = this.expressions.store(expression, metadata);
+    tempData.push(data.substring(cursor, nextStart));
+    tempData.push(placeholder);
+
+    if (saveWaypoints)
+      indent = newIndent + 1;
+    cursor = nextEnd;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+};
+
+ExpressionsProcessor.prototype.restore = function (data) {
+  var tempData = [];
+  var cursor = 0;
+
+  for (; cursor < data.length;) {
+    var nextMatch = this.expressions.nextMatch(data, cursor);
+    if (nextMatch.start < 0)
+      break;
+
+    tempData.push(data.substring(cursor, nextMatch.start));
+    var comment = this.expressions.restore(nextMatch.match);
+    tempData.push(comment);
+
+    cursor = nextMatch.end;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+};
+
+module.exports = ExpressionsProcessor;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/free-text-processor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/free-text-processor.js
new file mode 100644
index 0000000..8a42624
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/free-text-processor.js
@@ -0,0 +1,98 @@
+var EscapeStore = require('./escape-store');
+var QuoteScanner = require('../utils/quote-scanner');
+
+var lineBreak = require('os').EOL;
+
+function FreeTextProcessor(saveWaypoints) {
+  this.matches = new EscapeStore('FREE_TEXT');
+  this.saveWaypoints = saveWaypoints;
+}
+
+// Strip content tags by replacing them by the a special
+// marker for further restoring. It's done via string scanning
+// instead of regexps to speed up the process.
+FreeTextProcessor.prototype.escape = function (data) {
+  var self = this;
+  var breaksCount;
+  var lastBreakAt;
+  var indent;
+  var metadata;
+  var saveWaypoints = this.saveWaypoints;
+
+  return new QuoteScanner(data).each(function (match, store) {
+    if (saveWaypoints) {
+      breaksCount = match.split(lineBreak).length - 1;
+      lastBreakAt = match.lastIndexOf(lineBreak);
+      indent = lastBreakAt > 0 ?
+        match.substring(lastBreakAt + lineBreak.length).length :
+        match.length;
+      metadata = [breaksCount, indent];
+    }
+
+    var placeholder = self.matches.store(match, metadata);
+    store.push(placeholder);
+  });
+};
+
+function normalize(text, data, prefixContext, cursor) {
+  // FIXME: this is even a bigger hack now - see #407
+  var searchIn = data;
+  if (prefixContext) {
+    searchIn = prefixContext + data.substring(0, data.indexOf('__ESCAPED_FREE_TEXT_CLEAN_CSS'));
+    cursor = searchIn.length;
+  }
+
+  var lastSemicolon = searchIn.lastIndexOf(';', cursor);
+  var lastOpenBrace = searchIn.lastIndexOf('{', cursor);
+  var lastOne = 0;
+
+  if (lastSemicolon > -1 && lastOpenBrace > -1)
+    lastOne = Math.max(lastSemicolon, lastOpenBrace);
+  else if (lastSemicolon == -1)
+    lastOne = lastOpenBrace;
+  else
+    lastOne = lastSemicolon;
+
+  var context = searchIn.substring(lastOne + 1, cursor);
+
+  if (/\[[\w\d\-]+[\*\|\~\^\$]?=$/.test(context)) {
+    text = text
+      .replace(/\\\n|\\\r\n/g, '')
+      .replace(/\n|\r\n/g, '');
+  }
+
+  if (/^['"][a-zA-Z][a-zA-Z\d\-_]+['"]$/.test(text) && !/format\($/.test(context)) {
+    var isFont = /^(font|font\-family):/.test(context);
+    var isAttribute = /\[[\w\d\-]+[\*\|\~\^\$]?=$/.test(context);
+    var isKeyframe = /@(-moz-|-o-|-webkit-)?keyframes /.test(context);
+    var isAnimation = /^(-moz-|-o-|-webkit-)?animation(-name)?:/.test(context);
+
+    if (isFont || isAttribute || isKeyframe || isAnimation)
+      text = text.substring(1, text.length - 1);
+  }
+
+  return text;
+}
+
+FreeTextProcessor.prototype.restore = function (data, prefixContext) {
+  var tempData = [];
+  var cursor = 0;
+
+  for (; cursor < data.length;) {
+    var nextMatch = this.matches.nextMatch(data, cursor);
+    if (nextMatch.start < 0)
+      break;
+
+    tempData.push(data.substring(cursor, nextMatch.start));
+    var text = normalize(this.matches.restore(nextMatch.match), data, prefixContext, nextMatch.start);
+    tempData.push(text);
+
+    cursor = nextMatch.end;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+};
+
+module.exports = FreeTextProcessor;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/urls-processor.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/urls-processor.js
new file mode 100644
index 0000000..7e4a530
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/text/urls-processor.js
@@ -0,0 +1,72 @@
+var EscapeStore = require('./escape-store');
+var reduceUrls = require('../urls/reduce');
+
+var lineBreak = require('os').EOL;
+
+function UrlsProcessor(context, saveWaypoints, keepUrlQuotes) {
+  this.urls = new EscapeStore('URL');
+  this.context = context;
+  this.saveWaypoints = saveWaypoints;
+  this.keepUrlQuotes = keepUrlQuotes;
+}
+
+// Strip urls by replacing them by a special
+// marker for further restoring. It's done via string scanning
+// instead of regexps to speed up the process.
+UrlsProcessor.prototype.escape = function (data) {
+  var breaksCount;
+  var lastBreakAt;
+  var indent;
+  var saveWaypoints = this.saveWaypoints;
+  var self = this;
+
+  return reduceUrls(data, this.context, function (url, tempData) {
+    if (saveWaypoints) {
+      breaksCount = url.split(lineBreak).length - 1;
+      lastBreakAt = url.lastIndexOf(lineBreak);
+      indent = lastBreakAt > 0 ?
+        url.substring(lastBreakAt + lineBreak.length).length :
+        url.length;
+    }
+
+    var placeholder = self.urls.store(url, saveWaypoints ? [breaksCount, indent] : null);
+    tempData.push(placeholder);
+  });
+};
+
+function normalize(url, keepUrlQuotes) {
+  url = url
+    .replace(/^url/gi, 'url')
+    .replace(/\\?\n|\\?\r\n/g, '')
+    .replace(/(\s{2,}|\s)/g, ' ')
+    .replace(/^url\((['"])? /, 'url($1')
+    .replace(/ (['"])?\)$/, '$1)');
+
+  if (!keepUrlQuotes && !/^['"].+['"]$/.test(url) && !/url\(.*[\s\(\)].*\)/.test(url) && !/url\(['"]data:[^;]+;charset/.test(url))
+    url = url.replace(/["']/g, '');
+
+  return url;
+}
+
+UrlsProcessor.prototype.restore = function (data) {
+  var tempData = [];
+  var cursor = 0;
+
+  for (; cursor < data.length;) {
+    var nextMatch = this.urls.nextMatch(data, cursor);
+    if (nextMatch.start < 0)
+      break;
+
+    tempData.push(data.substring(cursor, nextMatch.start));
+    var url = normalize(this.urls.restore(nextMatch.match), this.keepUrlQuotes);
+    tempData.push(url);
+
+    cursor = nextMatch.end;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+};
+
+module.exports = UrlsProcessor;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/extract-properties.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/extract-properties.js
new file mode 100644
index 0000000..1805a84
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/extract-properties.js
@@ -0,0 +1,188 @@
+var split = require('../utils/split');
+
+var COMMA = ',';
+var FORWARD_SLASH = '/';
+
+var AT_RULE = 'at-rule';
+
+function selectorName(value) {
+  return value[0];
+}
+
+function noop() {}
+
+function withoutComments(string, into, heading, context) {
+  var matcher = heading ? /^__ESCAPED_COMMENT_/ : /__ESCAPED_COMMENT_/;
+  var track = heading ? context.track : noop; // don't track when comment not in a heading as we do it later in `trackComments`
+
+  while (matcher.test(string)) {
+    var startOfComment = string.indexOf('__');
+    var endOfComment = string.indexOf('__', startOfComment + 1) + 2;
+    var comment = string.substring(startOfComment, endOfComment);
+    string = string.substring(0, startOfComment) + string.substring(endOfComment);
+
+    track(comment);
+    into.push(comment);
+  }
+
+  return string;
+}
+
+function withoutHeadingComments(string, into, context) {
+  return withoutComments(string, into, true, context);
+}
+
+function withoutInnerComments(string, into, context) {
+  return withoutComments(string, into, false, context);
+}
+
+function trackComments(comments, into, context) {
+  for (var i = 0, l = comments.length; i < l; i++) {
+    context.track(comments[i]);
+    into.push(comments[i]);
+  }
+}
+
+function extractProperties(string, selectors, context) {
+  var list = [];
+  var innerComments = [];
+  var valueSeparator = /[ ,\/]/;
+
+  if (typeof string != 'string')
+    return [];
+
+  if (string.indexOf(')') > -1)
+    string = string.replace(/\)([^\s_;:,\)])/g, context.sourceMap ? ') __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ $1' : ') $1');
+
+  if (string.indexOf('ESCAPED_URL_CLEAN_CSS') > -1)
+    string = string.replace(/(ESCAPED_URL_CLEAN_CSS[^_]+?__)/g, context.sourceMap ? '$1 __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ ' : '$1 ');
+
+  var candidates = split(string, ';', false, '{', '}');
+
+  for (var i = 0, l = candidates.length; i < l; i++) {
+    var candidate = candidates[i];
+    var firstColonAt = candidate.indexOf(':');
+
+    var atRule = candidate.trim()[0] == '@';
+    if (atRule) {
+      context.track(candidate);
+      list.push([AT_RULE, candidate.trim()]);
+      continue;
+    }
+
+    if (firstColonAt == -1) {
+      context.track(candidate);
+      if (candidate.indexOf('__ESCAPED_COMMENT_SPECIAL') > -1)
+        list.push(candidate.trim());
+      continue;
+    }
+
+    if (candidate.indexOf('{') > 0 && candidate.indexOf('{') < firstColonAt) {
+      context.track(candidate);
+      continue;
+    }
+
+    var body = [];
+    var name = candidate.substring(0, firstColonAt);
+
+    innerComments = [];
+
+    if (name.indexOf('__ESCAPED_COMMENT') > -1)
+      name = withoutHeadingComments(name, list, context);
+
+    if (name.indexOf('__ESCAPED_COMMENT') > -1)
+      name = withoutInnerComments(name, innerComments, context);
+
+    body.push([name.trim()].concat(context.track(name, true)));
+    context.track(':');
+
+    trackComments(innerComments, list, context);
+
+    var firstBraceAt = candidate.indexOf('{');
+    var isVariable = name.trim().indexOf('--') === 0;
+    if (isVariable && firstBraceAt > 0) {
+      var blockPrefix = candidate.substring(firstColonAt + 1, firstBraceAt + 1);
+      var blockSuffix = candidate.substring(candidate.indexOf('}'));
+      var blockContent = candidate.substring(firstBraceAt + 1, candidate.length - blockSuffix.length);
+
+      context.track(blockPrefix);
+      body.push(extractProperties(blockContent, selectors, context));
+      list.push(body);
+      context.track(blockSuffix);
+      context.track(i < l - 1 ? ';' : '');
+
+      continue;
+    }
+
+    var values = split(candidate.substring(firstColonAt + 1), valueSeparator, true);
+
+    if (values.length == 1 && values[0] === '') {
+      context.warnings.push('Empty property \'' + name + '\' inside \'' + selectors.filter(selectorName).join(',') + '\' selector. Ignoring.');
+      continue;
+    }
+
+    for (var j = 0, m = values.length; j < m; j++) {
+      var value = values[j];
+      var trimmed = value.trim();
+
+      if (trimmed.length === 0)
+        continue;
+
+      var lastCharacter = trimmed[trimmed.length - 1];
+      var endsWithNonSpaceSeparator = trimmed.length > 1 && (lastCharacter == COMMA || lastCharacter == FORWARD_SLASH);
+
+      if (endsWithNonSpaceSeparator)
+        trimmed = trimmed.substring(0, trimmed.length - 1);
+
+      if (trimmed.indexOf('__ESCAPED_COMMENT_CLEAN_CSS(0,-') > -1) {
+        context.track(trimmed);
+        continue;
+      }
+
+      innerComments = [];
+
+      if (trimmed.indexOf('__ESCAPED_COMMENT') > -1)
+        trimmed = withoutHeadingComments(trimmed, list, context);
+
+      if (trimmed.indexOf('__ESCAPED_COMMENT') > -1)
+        trimmed = withoutInnerComments(trimmed, innerComments, context);
+
+      if (trimmed.length === 0) {
+        trackComments(innerComments, list, context);
+        continue;
+      }
+
+      var pos = body.length - 1;
+      if (trimmed == 'important' && body[pos][0] == '!') {
+        context.track(trimmed);
+        body[pos - 1][0] += '!important';
+        body.pop();
+        continue;
+      }
+
+      if (trimmed == '!important' || (trimmed == 'important' && body[pos][0][body[pos][0].length - 1] == '!')) {
+        context.track(trimmed);
+        body[pos][0] += trimmed;
+        continue;
+      }
+
+      body.push([trimmed].concat(context.track(value, true)));
+
+      trackComments(innerComments, list, context);
+
+      if (endsWithNonSpaceSeparator) {
+        body.push([lastCharacter]);
+        context.track(lastCharacter);
+      }
+    }
+
+    if (i < l - 1)
+      context.track(';');
+
+    list.push(body);
+  }
+
+  return list;
+}
+
+module.exports = extractProperties;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/extract-selectors.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/extract-selectors.js
new file mode 100644
index 0000000..dc8a47f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/extract-selectors.js
@@ -0,0 +1,17 @@
+var split = require('../utils/split');
+
+function extractSelectors(string, context) {
+  var list = [];
+  var metadata;
+  var selectors = split(string, ',');
+
+  for (var i = 0, l = selectors.length; i < l; i++) {
+    metadata = context.track(selectors[i], true, i);
+    context.track(',');
+    list.push([selectors[i].trim()].concat(metadata));
+  }
+
+  return list;
+}
+
+module.exports = extractSelectors;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/tokenize.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/tokenize.js
new file mode 100644
index 0000000..449a3cb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/tokenizer/tokenize.js
@@ -0,0 +1,264 @@
+var extractProperties = require('./extract-properties');
+var extractSelectors = require('./extract-selectors');
+var track = require('../source-maps/track');
+var split = require('../utils/split');
+
+var path = require('path');
+
+var flatBlock = /(@(font\-face|page|\-ms\-viewport|\-o\-viewport|viewport|counter\-style)|\\@.+?)/;
+
+function tokenize(data, outerContext) {
+  var chunks = split(normalize(data), '}', true, '{', '}');
+  if (chunks.length === 0)
+    return [];
+
+  var context = {
+    chunk: chunks.shift(),
+    chunks: chunks,
+    column: 0,
+    cursor: 0,
+    line: 1,
+    mode: 'top',
+    resolvePath: outerContext.options.explicitTarget ?
+      relativePathResolver(outerContext.options.root, outerContext.options.target) :
+      null,
+    source: undefined,
+    sourceMap: outerContext.options.sourceMap,
+    sourceMapInlineSources: outerContext.options.sourceMapInlineSources,
+    sourceMapTracker: outerContext.inputSourceMapTracker,
+    sourceReader: outerContext.sourceReader,
+    sourceTracker: outerContext.sourceTracker,
+    state: [],
+    track: outerContext.options.sourceMap ?
+      function (data, snapshotMetadata, fallbacks) { return [[track(data, context, snapshotMetadata, fallbacks)]]; } :
+      function () { return []; },
+    warnings: outerContext.warnings
+  };
+
+  return intoTokens(context);
+}
+
+function normalize(data) {
+  return data.replace(/\r\n/g, '\n');
+}
+
+function relativePathResolver(root, target) {
+  var rebaseTo = path.relative(root, target);
+
+  return function (relativeTo, sourcePath) {
+    return relativeTo != sourcePath ?
+      path.normalize(path.join(path.relative(rebaseTo, path.dirname(relativeTo)), sourcePath)) :
+      sourcePath;
+  };
+}
+
+function whatsNext(context) {
+  var mode = context.mode;
+  var chunk = context.chunk;
+  var closest;
+
+  if (chunk.length == context.cursor) {
+    if (context.chunks.length === 0)
+      return null;
+
+    context.chunk = chunk = context.chunks.shift();
+    context.cursor = 0;
+  }
+
+  if (mode == 'body') {
+    if (chunk[context.cursor] == '}')
+      return [context.cursor, 'bodyEnd'];
+
+    if (chunk.indexOf('}', context.cursor) == -1)
+      return null;
+
+    closest = context.cursor + split(chunk.substring(context.cursor - 1), '}', true, '{', '}')[0].length - 2;
+    return [closest, 'bodyEnd'];
+  }
+
+  var nextSpecial = chunk.indexOf('@', context.cursor);
+  var nextEscape = chunk.indexOf('__ESCAPED_', context.cursor);
+  var nextBodyStart = chunk.indexOf('{', context.cursor);
+  var nextBodyEnd = chunk.indexOf('}', context.cursor);
+
+  if (nextEscape > -1 && /\S/.test(chunk.substring(context.cursor, nextEscape)))
+    nextEscape = -1;
+
+  closest = nextSpecial;
+  if (closest == -1 || (nextEscape > -1 && nextEscape < closest))
+    closest = nextEscape;
+  if (closest == -1 || (nextBodyStart > -1 && nextBodyStart < closest))
+    closest = nextBodyStart;
+  if (closest == -1 || (nextBodyEnd > -1 && nextBodyEnd < closest))
+    closest = nextBodyEnd;
+
+  if (closest == -1)
+    return;
+  if (nextEscape === closest)
+    return [closest, 'escape'];
+  if (nextBodyStart === closest)
+    return [closest, 'bodyStart'];
+  if (nextBodyEnd === closest)
+    return [closest, 'bodyEnd'];
+  if (nextSpecial === closest)
+    return [closest, 'special'];
+}
+
+function intoTokens(context) {
+  var chunk = context.chunk;
+  var tokenized = [];
+  var newToken;
+  var value;
+
+  while (true) {
+    var next = whatsNext(context);
+    if (!next) {
+      var whatsLeft = context.chunk.substring(context.cursor);
+      if (whatsLeft.trim().length > 0) {
+        if (context.mode == 'body') {
+          context.warnings.push('Missing \'}\' after \'' + whatsLeft + '\'. Ignoring.');
+        } else {
+          tokenized.push(['text', [whatsLeft]]);
+        }
+        context.cursor += whatsLeft.length;
+      }
+      break;
+    }
+
+    var nextSpecial = next[0];
+    var what = next[1];
+    var nextEnd;
+    var oldMode;
+
+    chunk = context.chunk;
+
+    if (context.cursor != nextSpecial && what != 'bodyEnd') {
+      var spacing = chunk.substring(context.cursor, nextSpecial);
+      var leadingWhitespace = /^\s+/.exec(spacing);
+
+      if (leadingWhitespace) {
+        context.cursor += leadingWhitespace[0].length;
+        context.track(leadingWhitespace[0]);
+      }
+    }
+
+    if (what == 'special') {
+      var firstOpenBraceAt = chunk.indexOf('{', nextSpecial);
+      var firstSemicolonAt = chunk.indexOf(';', nextSpecial);
+      var isSingle = firstSemicolonAt > -1 && (firstOpenBraceAt == -1 || firstSemicolonAt < firstOpenBraceAt);
+      var isBroken = firstOpenBraceAt == -1 && firstSemicolonAt == -1;
+      if (isBroken) {
+        context.warnings.push('Broken declaration: \'' + chunk.substring(context.cursor) +  '\'.');
+        context.cursor = chunk.length;
+      } else if (isSingle) {
+        nextEnd = chunk.indexOf(';', nextSpecial + 1);
+        value = chunk.substring(context.cursor, nextEnd + 1);
+
+        tokenized.push([
+          'at-rule',
+          [value].concat(context.track(value, true))
+        ]);
+
+        context.track(';');
+        context.cursor = nextEnd + 1;
+      } else {
+        nextEnd = chunk.indexOf('{', nextSpecial + 1);
+        value = chunk.substring(context.cursor, nextEnd);
+
+        var trimmedValue = value.trim();
+        var isFlat = flatBlock.test(trimmedValue);
+        oldMode = context.mode;
+        context.cursor = nextEnd + 1;
+        context.mode = isFlat ? 'body' : 'block';
+
+        newToken = [
+          isFlat ? 'flat-block' : 'block'
+        ];
+
+        newToken.push([trimmedValue].concat(context.track(value, true)));
+        context.track('{');
+        newToken.push(intoTokens(context));
+
+        if (typeof newToken[2] == 'string')
+          newToken[2] = extractProperties(newToken[2], [[trimmedValue]], context);
+
+        context.mode = oldMode;
+        context.track('}');
+
+        tokenized.push(newToken);
+      }
+    } else if (what == 'escape') {
+      nextEnd = chunk.indexOf('__', nextSpecial + 1);
+      var escaped = chunk.substring(context.cursor, nextEnd + 2);
+      var isStartSourceMarker = !!context.sourceTracker.nextStart(escaped);
+      var isEndSourceMarker = !!context.sourceTracker.nextEnd(escaped);
+
+      if (isStartSourceMarker) {
+        context.track(escaped);
+        context.state.push({
+          source: context.source,
+          line: context.line,
+          column: context.column
+        });
+        context.source = context.sourceTracker.nextStart(escaped).filename;
+        context.line = 1;
+        context.column = 0;
+      } else if (isEndSourceMarker) {
+        var oldState = context.state.pop();
+        context.source = oldState.source;
+        context.line = oldState.line;
+        context.column = oldState.column;
+        context.track(escaped);
+      } else {
+        if (escaped.indexOf('__ESCAPED_COMMENT_SPECIAL') === 0)
+          tokenized.push(['text', [escaped]]);
+
+        context.track(escaped);
+      }
+
+      context.cursor = nextEnd + 2;
+    } else if (what == 'bodyStart') {
+      var selectors = extractSelectors(chunk.substring(context.cursor, nextSpecial), context);
+
+      oldMode = context.mode;
+      context.cursor = nextSpecial + 1;
+      context.mode = 'body';
+
+      var body = extractProperties(intoTokens(context), selectors, context);
+
+      context.track('{');
+      context.mode = oldMode;
+
+      tokenized.push([
+        'selector',
+        selectors,
+        body
+      ]);
+    } else if (what == 'bodyEnd') {
+      // extra closing brace at the top level can be safely ignored
+      if (context.mode == 'top') {
+        var at = context.cursor;
+        var warning = chunk[context.cursor] == '}' ?
+          'Unexpected \'}\' in \'' + chunk.substring(at - 20, at + 20) + '\'. Ignoring.' :
+          'Unexpected content: \'' + chunk.substring(at, nextSpecial + 1) + '\'. Ignoring.';
+
+        context.warnings.push(warning);
+        context.cursor = nextSpecial + 1;
+        continue;
+      }
+
+      if (context.mode == 'block')
+        context.track(chunk.substring(context.cursor, nextSpecial));
+      if (context.mode != 'block')
+        tokenized = chunk.substring(context.cursor, nextSpecial);
+
+      context.cursor = nextSpecial + 1;
+
+      break;
+    }
+  }
+
+  return tokenized;
+}
+
+module.exports = tokenize;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/rebase.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/rebase.js
new file mode 100644
index 0000000..04346b1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/rebase.js
@@ -0,0 +1,30 @@
+var path = require('path');
+
+var rewriteUrls = require('./rewrite');
+
+function rebaseUrls(data, context) {
+  var rebaseOpts = {
+    absolute: context.options.explicitRoot,
+    relative: !context.options.explicitRoot && context.options.explicitTarget,
+    fromBase: context.options.relativeTo
+  };
+
+  if (!rebaseOpts.absolute && !rebaseOpts.relative)
+    return data;
+
+  if (rebaseOpts.absolute && context.options.explicitTarget)
+    context.warnings.push('Both \'root\' and output file given so rebasing URLs as absolute paths');
+
+  if (rebaseOpts.absolute)
+    rebaseOpts.toBase = path.resolve(context.options.root);
+
+  if (rebaseOpts.relative)
+    rebaseOpts.toBase = path.resolve(context.options.target);
+
+  if (!rebaseOpts.fromBase || !rebaseOpts.toBase)
+    return data;
+
+  return rewriteUrls(data, rebaseOpts, context);
+}
+
+module.exports = rebaseUrls;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/reduce.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/reduce.js
new file mode 100644
index 0000000..daf4ee4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/reduce.js
@@ -0,0 +1,150 @@
+var URL_PREFIX = 'url(';
+var UPPERCASE_URL_PREFIX = 'URL(';
+var URL_SUFFIX = ')';
+var DATA_URI_PREFIX = 'data:';
+
+var IMPORT_URL_PREFIX = '@import';
+var UPPERCASE_IMPORT_URL_PREFIX = '@IMPORT';
+
+function byUrl(data, context, callback) {
+  var nextStart = 0;
+  var nextStartUpperCase = 0;
+  var nextEnd = 0;
+  var nextEndAhead = 0;
+  var isDataURI = false;
+  var cursor = 0;
+  var tempData = [];
+  var hasUppercaseUrl = data.indexOf(UPPERCASE_URL_PREFIX) > -1;
+
+  for (; nextEnd < data.length;) {
+    nextStart = data.indexOf(URL_PREFIX, nextEnd);
+    nextStartUpperCase = hasUppercaseUrl ? data.indexOf(UPPERCASE_URL_PREFIX, nextEnd) : -1;
+    if (nextStart == -1 && nextStartUpperCase == -1)
+      break;
+
+    if (nextStart == -1 && nextStartUpperCase > -1)
+      nextStart = nextStartUpperCase;
+
+
+    if (data[nextStart + URL_PREFIX.length] == '"') {
+      nextEnd = data.indexOf('"', nextStart + URL_PREFIX.length + 1);
+    } else if (data[nextStart + URL_PREFIX.length] == '\'') {
+      nextEnd = data.indexOf('\'', nextStart + URL_PREFIX.length + 1);
+    } else {
+      isDataURI = data.substring(nextStart + URL_PREFIX.length).trim().indexOf(DATA_URI_PREFIX) === 0;
+      nextEnd = data.indexOf(URL_SUFFIX, nextStart);
+
+      if (isDataURI) {
+        // this is a fuzzy matching logic for unqoted data URIs
+        while (true) {
+          nextEndAhead = data.indexOf(URL_SUFFIX, nextEnd + 1);
+          // if it has whitespace then we should be out of URL, otherwise keep iterating
+          // if it has not but content is not escaped, it has to be quoted so it will be captured
+          // by either of two clauses above
+          if (nextEndAhead == -1 || /\s/.test(data.substring(nextEnd, nextEndAhead)))
+            break;
+
+          nextEnd = nextEndAhead;
+        }
+      }
+    }
+
+
+    // Following lines are a safety mechanism to ensure
+    // incorrectly terminated urls are processed correctly.
+    if (nextEnd == -1) {
+      nextEnd = data.indexOf('}', nextStart);
+
+      if (nextEnd == -1)
+        nextEnd = data.length;
+      else
+        nextEnd--;
+
+      context.warnings.push('Broken URL declaration: \'' + data.substring(nextStart, nextEnd + 1) + '\'.');
+    } else {
+      if (data[nextEnd] != URL_SUFFIX)
+        nextEnd = data.indexOf(URL_SUFFIX, nextEnd);
+    }
+
+    tempData.push(data.substring(cursor, nextStart));
+
+    var url = data.substring(nextStart, nextEnd + 1);
+    callback(url, tempData);
+
+    cursor = nextEnd + 1;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+}
+
+function byImport(data, context, callback) {
+  var nextImport = 0;
+  var nextImportUpperCase = 0;
+  var nextStart = 0;
+  var nextEnd = 0;
+  var cursor = 0;
+  var tempData = [];
+  var nextSingleQuote = 0;
+  var nextDoubleQuote = 0;
+  var untilNextQuote;
+  var withQuote;
+  var SINGLE_QUOTE = '\'';
+  var DOUBLE_QUOTE = '"';
+
+  for (; nextEnd < data.length;) {
+    nextImport = data.indexOf(IMPORT_URL_PREFIX, nextEnd);
+    nextImportUpperCase = data.indexOf(UPPERCASE_IMPORT_URL_PREFIX, nextEnd);
+    if (nextImport == -1 && nextImportUpperCase == -1)
+      break;
+
+    if (nextImport > -1 && nextImportUpperCase > -1 && nextImportUpperCase < nextImport)
+      nextImport = nextImportUpperCase;
+
+    nextSingleQuote = data.indexOf(SINGLE_QUOTE, nextImport);
+    nextDoubleQuote = data.indexOf(DOUBLE_QUOTE, nextImport);
+
+    if (nextSingleQuote > -1 && nextDoubleQuote > -1 && nextSingleQuote < nextDoubleQuote) {
+      nextStart = nextSingleQuote;
+      withQuote = SINGLE_QUOTE;
+    } else if (nextSingleQuote > -1 && nextDoubleQuote > -1 && nextSingleQuote > nextDoubleQuote) {
+      nextStart = nextDoubleQuote;
+      withQuote = DOUBLE_QUOTE;
+    } else if (nextSingleQuote > -1) {
+      nextStart = nextSingleQuote;
+      withQuote = SINGLE_QUOTE;
+    } else if (nextDoubleQuote > -1) {
+      nextStart = nextDoubleQuote;
+      withQuote = DOUBLE_QUOTE;
+    } else {
+      break;
+    }
+
+    tempData.push(data.substring(cursor, nextStart));
+    nextEnd = data.indexOf(withQuote, nextStart + 1);
+
+    untilNextQuote = data.substring(nextImport, nextEnd);
+    if (nextEnd == -1 || /^@import\s+(url\(|__ESCAPED)/i.test(untilNextQuote)) {
+      cursor = nextStart;
+      break;
+    }
+
+    var url = data.substring(nextStart, nextEnd + 1);
+    callback(url, tempData);
+
+    cursor = nextEnd + 1;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+}
+
+function reduceAll(data, context, callback) {
+  data = byUrl(data, context, callback);
+  data = byImport(data, context, callback);
+  return data;
+}
+
+module.exports = reduceAll;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/rewrite.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/rewrite.js
new file mode 100644
index 0000000..bf49f19
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/urls/rewrite.js
@@ -0,0 +1,89 @@
+var path = require('path');
+var url = require('url');
+
+var reduceUrls = require('./reduce');
+
+var isWindows = process.platform == 'win32';
+
+function isAbsolute(uri) {
+  return uri[0] == '/';
+}
+
+function isSVGMarker(uri) {
+  return uri[0] == '#';
+}
+
+function isEscaped(uri) {
+  return uri.indexOf('__ESCAPED_URL_CLEAN_CSS__') === 0;
+}
+
+function isInternal(uri) {
+  return /^\w+:\w+/.test(uri);
+}
+
+function isRemote(uri) {
+  return /^[^:]+?:\/\//.test(uri) || uri.indexOf('//') === 0;
+}
+
+function isImport(uri) {
+  return uri.lastIndexOf('.css') === uri.length - 4;
+}
+
+function isData(uri) {
+  return uri.indexOf('data:') === 0;
+}
+
+function absolute(uri, options) {
+  return path
+    .resolve(path.join(options.fromBase, uri))
+    .replace(options.toBase, '');
+}
+
+function relative(uri, options) {
+  return path.relative(options.toBase, path.join(options.fromBase, uri));
+}
+
+function normalize(uri) {
+  return isWindows ? uri.replace(/\\/g, '/') : uri;
+}
+
+function rebase(uri, options) {
+  if (isAbsolute(uri) || isSVGMarker(uri) || isEscaped(uri) || isInternal(uri))
+    return uri;
+
+  if (options.rebase === false && !isImport(uri))
+    return uri;
+
+  if (!options.imports && isImport(uri))
+    return uri;
+
+  if (isData(uri))
+    return '\'' + uri + '\'';
+
+  if (isRemote(uri) || isRemote(options.toBase))
+    return url.resolve(options.toBase, uri);
+
+  return options.absolute ?
+    normalize(absolute(uri, options)) :
+    normalize(relative(uri, options));
+}
+
+function quoteFor(url) {
+  if (url.indexOf('\'') > -1)
+    return '"';
+  else if (url.indexOf('"') > -1)
+    return '\'';
+  else if (/\s/.test(url))
+    return '\'';
+  else
+    return '';
+}
+
+function rewriteUrls(data, options, context) {
+  return reduceUrls(data, context, function (url, tempData) {
+    url = url.replace(/^(url\()?\s*['"]?|['"]?\s*\)?$/g, '');
+    tempData.push('url(' + quoteFor(url) + rebase(url, options) + quoteFor(url) + ')');
+  });
+}
+
+module.exports = rewriteUrls;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/compatibility.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/compatibility.js
new file mode 100644
index 0000000..34a8269
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/compatibility.js
@@ -0,0 +1,159 @@
+var util = require('util');
+
+var DEFAULTS = {
+  '*': {
+    colors: {
+      opacity: true // rgba / hsla
+    },
+    properties: {
+      backgroundClipMerging: false, // background-clip to shorthand
+      backgroundOriginMerging: false, // background-origin to shorthand
+      backgroundSizeMerging: false, // background-size to shorthand
+      colors: true, // any kind of color transformations, like `#ff00ff` to `#f0f` or `#fff` into `red`
+      ieBangHack: false, // !ie suffix hacks on IE<8
+      iePrefixHack: false, // underscore / asterisk prefix hacks on IE
+      ieSuffixHack: true, // \9 suffix hacks on IE6-9
+      merging: true, // merging properties into one
+      spaceAfterClosingBrace: true, // 'url() no-repeat' to 'url()no-repeat'
+      urlQuotes: false, // whether to wrap content of `url()` into quotes or not
+      zeroUnits: true // 0[unit] -> 0
+    },
+    selectors: {
+      adjacentSpace: false, // div+ nav Android stock browser hack
+      ie7Hack: false, // *+html hack
+      special: /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:dir\([a-z-]*\)|:first(?![a-z-])|:fullscreen|:left|:read-only|:read-write|:right|:placeholder|:host|::content|\/deep\/|::shadow)/ // special selectors which prevent merging
+    },
+    units: {
+      ch: true,
+      in: true,
+      pc: true,
+      pt: true,
+      rem: true,
+      vh: true,
+      vm: true, // vm is vmin on IE9+ see https://developer.mozilla.org/en-US/docs/Web/CSS/length
+      vmax: true,
+      vmin: true,
+      vw: true
+    }
+  },
+  'ie8': {
+    colors: {
+      opacity: false
+    },
+    properties: {
+      backgroundClipMerging: false,
+      backgroundOriginMerging: false,
+      backgroundSizeMerging: false,
+      colors: true,
+      ieBangHack: false,
+      iePrefixHack: true,
+      ieSuffixHack: true,
+      merging: false,
+      spaceAfterClosingBrace: true,
+      urlQuotes: false,
+      zeroUnits: true
+    },
+    selectors: {
+      adjacentSpace: false,
+      ie7Hack: false,
+      special: /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not|:placeholder|:host|::content|\/deep\/|::shadow)/
+    },
+    units: {
+      ch: false,
+      in: true,
+      pc: true,
+      pt: true,
+      rem: false,
+      vh: false,
+      vm: false,
+      vmax: false,
+      vmin: false,
+      vw: false
+    }
+  },
+  'ie7': {
+    colors: {
+      opacity: false
+    },
+    properties: {
+      backgroundClipMerging: false,
+      backgroundOriginMerging: false,
+      backgroundSizeMerging: false,
+      colors: true,
+      ieBangHack: true,
+      iePrefixHack: true,
+      ieSuffixHack: true,
+      merging: false,
+      spaceAfterClosingBrace: true,
+      urlQuotes: false,
+      zeroUnits: true
+    },
+    selectors: {
+      adjacentSpace: false,
+      ie7Hack: true,
+      special: /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:focus|:before|:after|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not|:placeholder|:host|::content|\/deep\/|::shadow)/
+    },
+    units: {
+      ch: false,
+      in: true,
+      pc: true,
+      pt: true,
+      rem: false,
+      vh: false,
+      vm: false,
+      vmax: false,
+      vmin: false,
+      vw: false,
+    }
+  }
+};
+
+function Compatibility(source) {
+  this.source = source || {};
+}
+
+function merge(source, target) {
+  for (var key in source) {
+    var value = source[key];
+
+    if (typeof value === 'object' && !util.isRegExp(value))
+      target[key] = merge(value, target[key] || {});
+    else
+      target[key] = key in target ? target[key] : value;
+  }
+
+  return target;
+}
+
+function calculateSource(source) {
+  if (typeof source == 'object')
+    return source;
+
+  if (!/[,\+\-]/.test(source))
+    return DEFAULTS[source] || DEFAULTS['*'];
+
+  var parts = source.split(',');
+  var template = parts[0] in DEFAULTS ?
+    DEFAULTS[parts.shift()] :
+    DEFAULTS['*'];
+
+  source = {};
+
+  parts.forEach(function (part) {
+    var isAdd = part[0] == '+';
+    var key = part.substring(1).split('.');
+    var group = key[0];
+    var option = key[1];
+
+    source[group] = source[group] || {};
+    source[group][option] = isAdd;
+  });
+
+  return merge(template, source);
+}
+
+Compatibility.prototype.toOptions = function () {
+  return merge(DEFAULTS['*'], calculateSource(this.source));
+};
+
+module.exports = Compatibility;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/input-source-map-tracker.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/input-source-map-tracker.js
new file mode 100644
index 0000000..7db9eb9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/input-source-map-tracker.js
@@ -0,0 +1,284 @@
+var SourceMapConsumer = require('source-map').SourceMapConsumer;
+
+var fs = require('fs');
+var path = require('path');
+var http = require('http');
+var https = require('https');
+var url = require('url');
+
+var override = require('../utils/object.js').override;
+
+var MAP_MARKER = /\/\*# sourceMappingURL=(\S+) \*\//;
+var REMOTE_RESOURCE = /^(https?:)?\/\//;
+var DATA_URI = /^data:(\S*?)?(;charset=[^;]+)?(;[^,]+?)?,(.+)/;
+
+var unescape = global.unescape;
+
+function InputSourceMapStore(outerContext) {
+  this.options = outerContext.options;
+  this.errors = outerContext.errors;
+  this.warnings = outerContext.warnings;
+  this.sourceTracker = outerContext.sourceTracker;
+  this.timeout = this.options.inliner.timeout;
+  this.requestOptions = this.options.inliner.request;
+  this.localOnly = outerContext.localOnly;
+  this.relativeTo = outerContext.options.target || process.cwd();
+
+  this.maps = {};
+  this.sourcesContent = {};
+}
+
+function fromString(self, _, whenDone) {
+  self.trackLoaded(undefined, undefined, self.options.sourceMap);
+  return whenDone();
+}
+
+function fromSource(self, data, whenDone, context) {
+  var nextAt = 0;
+
+  function proceedToNext() {
+    context.cursor += nextAt + 1;
+    fromSource(self, data, whenDone, context);
+  }
+
+  while (context.cursor < data.length) {
+    var fragment = data.substring(context.cursor);
+
+    var markerStartMatch = self.sourceTracker.nextStart(fragment) || { index: -1 };
+    var markerEndMatch = self.sourceTracker.nextEnd(fragment) || { index: -1 };
+    var mapMatch = MAP_MARKER.exec(fragment) || { index: -1 };
+    var sourceMapFile = mapMatch[1];
+
+    nextAt = data.length;
+    if (markerStartMatch.index > -1)
+      nextAt = markerStartMatch.index;
+    if (markerEndMatch.index > -1 && markerEndMatch.index < nextAt)
+      nextAt = markerEndMatch.index;
+    if (mapMatch.index > -1 && mapMatch.index < nextAt)
+      nextAt = mapMatch.index;
+
+    if (nextAt == data.length)
+      break;
+
+    if (nextAt == markerStartMatch.index) {
+      context.files.push(markerStartMatch.filename);
+    } else if (nextAt == markerEndMatch.index) {
+      context.files.pop();
+    } else if (nextAt == mapMatch.index) {
+      var isRemote = /^https?:\/\//.test(sourceMapFile) || /^\/\//.test(sourceMapFile);
+      var isDataUri = DATA_URI.test(sourceMapFile);
+
+      if (isRemote) {
+        return fetchMapFile(self, sourceMapFile, context, proceedToNext);
+      } else {
+        var sourceFile = context.files[context.files.length - 1];
+        var sourceMapPath, sourceMapData;
+        var sourceDir = sourceFile ? path.dirname(sourceFile) : self.options.relativeTo;
+
+        if (isDataUri) {
+          // source map's path is the same as the source file it comes from
+          sourceMapPath = path.resolve(self.options.root, sourceFile || '');
+          sourceMapData = fromDataUri(sourceMapFile);
+        } else {
+          sourceMapPath = path.resolve(self.options.root, path.join(sourceDir || '', sourceMapFile));
+          sourceMapData = fs.readFileSync(sourceMapPath, 'utf-8');
+        }
+        self.trackLoaded(sourceFile || undefined, sourceMapPath, sourceMapData);
+      }
+    }
+
+    context.cursor += nextAt + 1;
+  }
+
+  return whenDone();
+}
+
+function fromDataUri(uriString) {
+  var match = DATA_URI.exec(uriString);
+  var charset = match[2] ? match[2].split(/[=;]/)[2] : 'us-ascii';
+  var encoding = match[3] ? match[3].split(';')[1] : 'utf8';
+  var data = encoding == 'utf8' ? unescape(match[4]) : match[4];
+
+  var buffer = new Buffer(data, encoding);
+  buffer.charset = charset;
+
+  return buffer.toString();
+}
+
+function fetchMapFile(self, sourceUrl, context, done) {
+  fetch(self, sourceUrl, function (data) {
+    self.trackLoaded(context.files[context.files.length - 1] || undefined, sourceUrl, data);
+    done();
+  }, function (message) {
+    context.errors.push('Broken source map at "' + sourceUrl + '" - ' + message);
+    return done();
+  });
+}
+
+function fetch(self, path, onSuccess, onFailure) {
+  var protocol = path.indexOf('https') === 0 ? https : http;
+  var requestOptions = override(url.parse(path), self.requestOptions);
+  var errorHandled = false;
+
+  protocol
+    .get(requestOptions, function (res) {
+      if (res.statusCode < 200 || res.statusCode > 299)
+        return onFailure(res.statusCode);
+
+      var chunks = [];
+      res.on('data', function (chunk) {
+        chunks.push(chunk.toString());
+      });
+      res.on('end', function () {
+        onSuccess(chunks.join(''));
+      });
+    })
+    .on('error', function (res) {
+      if (errorHandled)
+        return;
+
+      onFailure(res.message);
+      errorHandled = true;
+    })
+    .on('timeout', function () {
+      if (errorHandled)
+        return;
+
+      onFailure('timeout');
+      errorHandled = true;
+    })
+    .setTimeout(self.timeout);
+}
+
+function originalPositionIn(trackedSource, line, column, token, allowNFallbacks) {
+  var originalPosition;
+  var maxRange = token.length;
+  var position = {
+    line: line,
+    column: column + maxRange
+  };
+
+  while (maxRange-- > 0) {
+    position.column--;
+    originalPosition = trackedSource.data.originalPositionFor(position);
+
+    if (originalPosition)
+      break;
+  }
+
+  if (originalPosition.line === null && line > 1 && allowNFallbacks > 0)
+    return originalPositionIn(trackedSource, line - 1, column, token, allowNFallbacks - 1);
+
+  if (trackedSource.path && originalPosition.source) {
+    originalPosition.source = REMOTE_RESOURCE.test(trackedSource.path) ?
+      url.resolve(trackedSource.path, originalPosition.source) :
+      path.join(trackedSource.path, originalPosition.source);
+
+    originalPosition.sourceResolved = true;
+  }
+
+  return originalPosition;
+}
+
+function trackContentSources(self, sourceFile) {
+  var consumer = self.maps[sourceFile].data;
+  var isRemote = REMOTE_RESOURCE.test(sourceFile);
+  var sourcesMapping = {};
+
+  consumer.sources.forEach(function (file, index) {
+    var uniquePath = isRemote ?
+      url.resolve(path.dirname(sourceFile), file) :
+      path.relative(self.relativeTo, path.resolve(path.dirname(sourceFile), file));
+
+    sourcesMapping[uniquePath] = consumer.sourcesContent && consumer.sourcesContent[index];
+  });
+  self.sourcesContent[sourceFile] = sourcesMapping;
+}
+
+function _resolveSources(self, remaining, whenDone) {
+  function processNext() {
+    return _resolveSources(self, remaining, whenDone);
+  }
+
+  if (remaining.length === 0)
+    return whenDone();
+
+  var current = remaining.shift();
+  var sourceFile = current[0];
+  var originalFile = current[1];
+  var isRemote = REMOTE_RESOURCE.test(sourceFile);
+
+  if (isRemote && self.localOnly) {
+    self.warnings.push('No callback given to `#minify` method, cannot fetch a remote file from "' + originalFile + '"');
+    return processNext();
+  }
+
+  if (isRemote) {
+    fetch(self, originalFile, function (data) {
+      self.sourcesContent[sourceFile][originalFile] = data;
+      processNext();
+    }, function (message) {
+      self.warnings.push('Broken original source file at "' + originalFile + '" - ' + message);
+      processNext();
+    });
+  } else {
+    var fullPath = path.join(self.options.root, originalFile);
+    if (fs.existsSync(fullPath))
+      self.sourcesContent[sourceFile][originalFile] = fs.readFileSync(fullPath, 'utf-8');
+    else
+      self.warnings.push('Missing original source file at "' + fullPath + '".');
+    return processNext();
+  }
+}
+
+InputSourceMapStore.prototype.track = function (data, whenDone) {
+  return typeof this.options.sourceMap == 'string' ?
+    fromString(this, data, whenDone) :
+    fromSource(this, data, whenDone, { files: [], cursor: 0, errors: this.errors });
+};
+
+InputSourceMapStore.prototype.trackLoaded = function (sourcePath, mapPath, mapData) {
+  var relativeTo = this.options.explicitTarget ? this.options.target : this.options.root;
+  var isRemote = REMOTE_RESOURCE.test(sourcePath);
+
+  if (mapPath) {
+    mapPath = isRemote ?
+      path.dirname(mapPath) :
+      path.dirname(path.relative(relativeTo, mapPath));
+  }
+
+  this.maps[sourcePath] = {
+    path: mapPath,
+    data: new SourceMapConsumer(mapData)
+  };
+
+  trackContentSources(this, sourcePath);
+};
+
+InputSourceMapStore.prototype.isTracking = function (source) {
+  return !!this.maps[source];
+};
+
+InputSourceMapStore.prototype.originalPositionFor = function (sourceInfo, token, allowNFallbacks) {
+  return originalPositionIn(this.maps[sourceInfo.source], sourceInfo.line, sourceInfo.column, token, allowNFallbacks);
+};
+
+InputSourceMapStore.prototype.sourcesContentFor = function (contextSource) {
+  return this.sourcesContent[contextSource];
+};
+
+InputSourceMapStore.prototype.resolveSources = function (whenDone) {
+  var toResolve = [];
+
+  for (var sourceFile in this.sourcesContent) {
+    var contents = this.sourcesContent[sourceFile];
+    for (var originalFile in contents) {
+      if (!contents[originalFile])
+        toResolve.push([sourceFile, originalFile]);
+    }
+  }
+
+  return _resolveSources(this, toResolve, whenDone);
+};
+
+module.exports = InputSourceMapStore;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/object.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/object.js
new file mode 100644
index 0000000..8e94886
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/object.js
@@ -0,0 +1,11 @@
+module.exports = {
+  override: function (source1, source2) {
+    var target = {};
+    for (var key1 in source1)
+      target[key1] = source1[key1];
+    for (var key2 in source2)
+      target[key2] = source2[key2];
+
+    return target;
+  }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/quote-scanner.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/quote-scanner.js
new file mode 100644
index 0000000..69980f5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/quote-scanner.js
@@ -0,0 +1,102 @@
+function QuoteScanner(data) {
+  this.data = data;
+}
+
+var findQuoteEnd = function (data, matched, cursor, oldCursor) {
+  var commentStartMark = '/*';
+  var commentEndMark = '*/';
+  var escapeMark = '\\';
+  var blockEndMark = '}';
+  var dataPrefix = data.substring(oldCursor, cursor);
+  var commentEndedAt = dataPrefix.lastIndexOf(commentEndMark, cursor);
+  var commentStartedAt = dataPrefix.lastIndexOf(commentStartMark, cursor);
+  var commentStarted = false;
+
+  if (commentEndedAt >= cursor && commentStartedAt > -1)
+    commentStarted = true;
+  if (commentStartedAt < cursor && commentStartedAt > commentEndedAt)
+    commentStarted = true;
+
+  if (commentStarted) {
+    var commentEndsAt = data.indexOf(commentEndMark, cursor);
+    if (commentEndsAt > -1)
+      return commentEndsAt;
+
+    commentEndsAt = data.indexOf(blockEndMark, cursor);
+    return commentEndsAt > -1 ? commentEndsAt - 1 : data.length;
+  }
+
+  while (true) {
+    if (data[cursor] === undefined)
+      break;
+    if (data[cursor] == matched && (data[cursor - 1] != escapeMark || data[cursor - 2] == escapeMark))
+      break;
+
+    cursor++;
+  }
+
+  return cursor;
+};
+
+function findNext(data, mark, startAt) {
+  var escapeMark = '\\';
+  var candidate = startAt;
+
+  while (true) {
+    candidate = data.indexOf(mark, candidate + 1);
+    if (candidate == -1)
+      return -1;
+    if (data[candidate - 1] != escapeMark)
+      return candidate;
+  }
+}
+
+QuoteScanner.prototype.each = function (callback) {
+  var data = this.data;
+  var tempData = [];
+  var nextStart = 0;
+  var nextEnd = 0;
+  var cursor = 0;
+  var matchedMark = null;
+  var singleMark = '\'';
+  var doubleMark = '"';
+  var dataLength = data.length;
+
+  for (; nextEnd < data.length;) {
+    var nextStartSingle = findNext(data, singleMark, nextEnd);
+    var nextStartDouble = findNext(data, doubleMark, nextEnd);
+
+    if (nextStartSingle == -1)
+      nextStartSingle = dataLength;
+    if (nextStartDouble == -1)
+      nextStartDouble = dataLength;
+
+    if (nextStartSingle < nextStartDouble) {
+      nextStart = nextStartSingle;
+      matchedMark = singleMark;
+    } else {
+      nextStart = nextStartDouble;
+      matchedMark = doubleMark;
+    }
+
+    if (nextStart == -1)
+      break;
+
+    nextEnd = findQuoteEnd(data, matchedMark, nextStart + 1, cursor);
+    if (nextEnd == -1)
+      break;
+
+    var text = data.substring(nextStart, nextEnd + 1);
+    tempData.push(data.substring(cursor, nextStart));
+    if (text.length > 0)
+      callback(text, tempData, nextStart);
+
+    cursor = nextEnd + 1;
+  }
+
+  return tempData.length > 0 ?
+    tempData.join('') + data.substring(cursor, data.length) :
+    data;
+};
+
+module.exports = QuoteScanner;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/source-reader.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/source-reader.js
new file mode 100644
index 0000000..267a069
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/source-reader.js
@@ -0,0 +1,95 @@
+var path = require('path');
+var rewriteUrls = require('../urls/rewrite');
+
+var REMOTE_RESOURCE = /^(https?:)?\/\//;
+
+function SourceReader(context, data) {
+  this.outerContext = context;
+  this.data = data;
+  this.sources = {};
+}
+
+SourceReader.prototype.sourceAt = function (path) {
+  return this.sources[path];
+};
+
+SourceReader.prototype.trackSource = function (path, source) {
+  this.sources[path] = {};
+  this.sources[path][path] = source;
+};
+
+SourceReader.prototype.toString = function () {
+  if (typeof this.data == 'string')
+    return fromString(this);
+  if (Buffer.isBuffer(this.data))
+    return fromBuffer(this);
+  if (Array.isArray(this.data))
+    return fromArray(this);
+
+  return fromHash(this);
+};
+
+function fromString(self) {
+  var data = self.data;
+  self.trackSource(undefined, data);
+  return data;
+}
+
+function fromBuffer(self) {
+  var data = self.data.toString();
+  self.trackSource(undefined, data);
+  return data;
+}
+
+function fromArray(self) {
+  return self.data
+    .map(function (source) {
+      return self.outerContext.options.processImport === false ?
+        source + '@shallow' :
+        source;
+    })
+    .map(function (source) {
+      return !self.outerContext.options.relativeTo || /^https?:\/\//.test(source) ?
+        source :
+        path.relative(self.outerContext.options.relativeTo, source);
+    })
+    .map(function (source) { return '@import url(' + source + ');'; })
+    .join('');
+}
+
+function fromHash(self) {
+  var data = [];
+  var toBase = path.resolve(self.outerContext.options.target || self.outerContext.options.root);
+
+  for (var source in self.data) {
+    var styles = self.data[source].styles;
+    var inputSourceMap = self.data[source].sourceMap;
+    var isRemote = REMOTE_RESOURCE.test(source);
+    var absoluteSource = isRemote ? source : path.resolve(source);
+    var absoluteSourcePath = path.dirname(absoluteSource);
+
+    var rewriteOptions = {
+      absolute: self.outerContext.options.explicitRoot,
+      relative: !self.outerContext.options.explicitRoot,
+      imports: true,
+      rebase: self.outerContext.options.rebase,
+      fromBase: absoluteSourcePath,
+      toBase: isRemote ? absoluteSourcePath : toBase
+    };
+    styles = rewriteUrls(styles, rewriteOptions, self.outerContext);
+
+    self.trackSource(source, styles);
+
+    styles = self.outerContext.sourceTracker.store(source, styles);
+
+    // here we assume source map lies in the same directory as `source` does
+    if (self.outerContext.options.sourceMap && inputSourceMap)
+      self.outerContext.inputSourceMapTracker.trackLoaded(source, source, inputSourceMap);
+
+    data.push(styles);
+  }
+
+  return data.join('');
+}
+
+module.exports = SourceReader;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/source-tracker.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/source-tracker.js
new file mode 100644
index 0000000..4cc9b6d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/source-tracker.js
@@ -0,0 +1,31 @@
+function SourceTracker() {
+  this.sources = [];
+}
+
+SourceTracker.prototype.store = function (filename, data) {
+  this.sources.push(filename);
+
+  return '__ESCAPED_SOURCE_CLEAN_CSS' + (this.sources.length - 1) + '__' +
+    data +
+    '__ESCAPED_SOURCE_END_CLEAN_CSS__';
+};
+
+SourceTracker.prototype.nextStart = function (data) {
+  var next = /__ESCAPED_SOURCE_CLEAN_CSS(\d+)__/.exec(data);
+
+  return next ?
+    { index: next.index, filename: this.sources[~~next[1]] } :
+    null;
+};
+
+SourceTracker.prototype.nextEnd = function (data) {
+  return /__ESCAPED_SOURCE_END_CLEAN_CSS__/g.exec(data);
+};
+
+SourceTracker.prototype.removeAll = function (data) {
+  return data
+    .replace(/__ESCAPED_SOURCE_CLEAN_CSS\d+__/g, '')
+    .replace(/__ESCAPED_SOURCE_END_CLEAN_CSS__/g, '');
+};
+
+module.exports = SourceTracker;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/split.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/split.js
new file mode 100644
index 0000000..0faf745
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/lib/utils/split.js
@@ -0,0 +1,49 @@
+function split(value, separator, includeSeparator, openLevel, closeLevel) {
+  var withRegex = typeof separator != 'string';
+  var hasSeparator = withRegex ?
+    separator.test(value) :
+    value.indexOf(separator);
+
+  if (!hasSeparator)
+    return [value];
+
+  openLevel = openLevel || '(';
+  closeLevel = closeLevel || ')';
+
+  if (value.indexOf(openLevel) == -1 && !includeSeparator)
+    return value.split(separator);
+
+  var level = 0;
+  var cursor = 0;
+  var lastStart = 0;
+  var len = value.length;
+  var tokens = [];
+
+  while (cursor < len) {
+    if (value[cursor] == openLevel) {
+      level++;
+    } else if (value[cursor] == closeLevel) {
+      level--;
+    }
+
+    if (level === 0 && cursor > 0 && cursor + 1 < len && (withRegex ? separator.test(value[cursor]) : value[cursor] == separator)) {
+      tokens.push(value.substring(lastStart, cursor + (includeSeparator ? 1 : 0)));
+      lastStart = cursor + 1;
+    }
+
+    cursor++;
+  }
+
+  if (lastStart < cursor + 1) {
+    var lastValue = value.substring(lastStart);
+    var lastCharacter = lastValue[lastValue.length - 1];
+    if (!includeSeparator && (withRegex ? separator.test(lastCharacter) : lastCharacter == separator))
+      lastValue = lastValue.substring(0, lastValue.length - 1);
+
+    tokens.push(lastValue);
+  }
+
+  return tokens;
+}
+
+module.exports = split;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/History.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/History.md
new file mode 100644
index 0000000..7b8b2c4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/History.md
@@ -0,0 +1,256 @@
+
+2.8.1 / 2015-04-22
+==================
+
+ * Back out `support multiline description` Close #396 #397
+
+
+
+2.8.0 / 2015-04-07
+==================
+
+  * Add `process.execArg` support, execution args like `--harmony` will be passed to sub-commands #387 @DigitalIO @zhiyelee
+  * Fix bug in Git-style sub-commands #372 @zhiyelee
+  * Allow commands to be hidden from help #383 @tonylukasavage
+  * When git-style sub-commands are in use, yet none are called, display help #382 @claylo
+  * Add ability to specify arguments syntax for top-level command #258 @rrthomas
+  * Support multiline descriptions #208 @zxqfox
+
+2.7.1 / 2015-03-11
+==================
+
+ * Revert #347 (fix collisions when option and first arg have same name) which causes a bug in #367.
+
+2.7.0 / 2015-03-09
+==================
+
+ * Fix git-style bug when installed globally. Close #335 #349 @zhiyelee
+ * Fix collisions when option and first arg have same name. Close #346 #347 @tonylukasavage
+ * Add support for camelCase on `opts()`. Close #353  @nkzawa
+ * Add node.js 0.12 and io.js to travis.yml
+ * Allow RegEx options. #337 @palanik
+ * Fixes exit code when sub-command failing.  Close #260 #332 @pirelenito
+ * git-style `bin` files in $PATH make sense. Close #196 #327  @zhiyelee
+
+2.6.0 / 2014-12-30
+==================
+
+  * added `Command#allowUnknownOption` method. Close #138 #318 @doozr @zhiyelee
+  * Add application description to the help msg. Close #112 @dalssoft
+
+2.5.1 / 2014-12-15
+==================
+
+  * fixed two bugs incurred by variadic arguments. Close #291 @Quentin01 #302 @zhiyelee
+
+2.5.0 / 2014-10-24
+==================
+
+ * add support for variadic arguments. Closes #277 @whitlockjc
+
+2.4.0 / 2014-10-17
+==================
+
+ * fixed a bug on executing the coercion function of subcommands option. Closes #270
+ * added `Command.prototype.name` to retrieve command name. Closes #264 #266 @tonylukasavage
+ * added `Command.prototype.opts` to retrieve all the options as a simple object of key-value pairs. Closes #262 @tonylukasavage
+ * fixed a bug on subcommand name. Closes #248 @jonathandelgado
+ * fixed function normalize doesn’t honor option terminator. Closes #216 @abbr
+
+2.3.0 / 2014-07-16
+==================
+
+ * add command alias'. Closes PR #210
+ * fix: Typos. Closes #99
+ * fix: Unused fs module. Closes #217
+
+2.2.0 / 2014-03-29
+==================
+
+ * add passing of previous option value
+ * fix: support subcommands on windows. Closes #142
+ * Now the defaultValue passed as the second argument of the coercion function.
+
+2.1.0 / 2013-11-21
+==================
+
+ * add: allow cflag style option params, unit test, fixes #174
+
+2.0.0 / 2013-07-18
+==================
+
+ * remove input methods (.prompt, .confirm, etc)
+
+1.3.2 / 2013-07-18
+==================
+
+ * add support for sub-commands to co-exist with the original command
+
+1.3.1 / 2013-07-18
+==================
+
+ * add quick .runningCommand hack so you can opt-out of other logic when running a sub command
+
+1.3.0 / 2013-07-09
+==================
+
+ * add EACCES error handling
+ * fix sub-command --help
+
+1.2.0 / 2013-06-13
+==================
+
+ * allow "-" hyphen as an option argument
+ * support for RegExp coercion
+
+1.1.1 / 2012-11-20
+==================
+
+  * add more sub-command padding
+  * fix .usage() when args are present. Closes #106
+
+1.1.0 / 2012-11-16
+==================
+
+  * add git-style executable subcommand support. Closes #94
+
+1.0.5 / 2012-10-09
+==================
+
+  * fix `--name` clobbering. Closes #92
+  * fix examples/help. Closes #89
+
+1.0.4 / 2012-09-03
+==================
+
+  * add `outputHelp()` method.
+
+1.0.3 / 2012-08-30
+==================
+
+  * remove invalid .version() defaulting
+
+1.0.2 / 2012-08-24
+==================
+
+  * add `--foo=bar` support [arv]
+  * fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]
+
+1.0.1 / 2012-08-03
+==================
+
+  * fix issue #56
+  * fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())
+
+1.0.0 / 2012-07-05
+==================
+
+  * add support for optional option descriptions
+  * add defaulting of `.version()` to package.json's version
+
+0.6.1 / 2012-06-01
+==================
+
+  * Added: append (yes or no) on confirmation
+  * Added: allow node.js v0.7.x
+
+0.6.0 / 2012-04-10
+==================
+
+  * Added `.prompt(obj, callback)` support. Closes #49
+  * Added default support to .choose(). Closes #41
+  * Fixed the choice example
+
+0.5.1 / 2011-12-20
+==================
+
+  * Fixed `password()` for recent nodes. Closes #36
+
+0.5.0 / 2011-12-04
+==================
+
+  * Added sub-command option support [itay]
+
+0.4.3 / 2011-12-04
+==================
+
+  * Fixed custom help ordering. Closes #32
+
+0.4.2 / 2011-11-24
+==================
+
+  * Added travis support
+  * Fixed: line-buffered input automatically trimmed. Closes #31
+
+0.4.1 / 2011-11-18
+==================
+
+  * Removed listening for "close" on --help
+
+0.4.0 / 2011-11-15
+==================
+
+  * Added support for `--`. Closes #24
+
+0.3.3 / 2011-11-14
+==================
+
+  * Fixed: wait for close event when writing help info [Jerry Hamlet]
+
+0.3.2 / 2011-11-01
+==================
+
+  * Fixed long flag definitions with values [felixge]
+
+0.3.1 / 2011-10-31
+==================
+
+  * Changed `--version` short flag to `-V` from `-v`
+  * Changed `.version()` so it's configurable [felixge]
+
+0.3.0 / 2011-10-31
+==================
+
+  * Added support for long flags only. Closes #18
+
+0.2.1 / 2011-10-24
+==================
+
+  * "node": ">= 0.4.x < 0.7.0". Closes #20
+
+0.2.0 / 2011-09-26
+==================
+
+  * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]
+
+0.1.0 / 2011-08-24
+==================
+
+  * Added support for custom `--help` output
+
+0.0.5 / 2011-08-18
+==================
+
+  * Changed: when the user enters nothing prompt for password again
+  * Fixed issue with passwords beginning with numbers [NuckChorris]
+
+0.0.4 / 2011-08-15
+==================
+
+  * Fixed `Commander#args`
+
+0.0.3 / 2011-08-15
+==================
+
+  * Added default option value support
+
+0.0.2 / 2011-08-15
+==================
+
+  * Added mask support to `Command#password(str[, mask], fn)`
+  * Added `Command#password(str, fn)`
+
+0.0.1 / 2010-01-03
+==================
+
+  * Initial release
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/LICENSE
new file mode 100644
index 0000000..10f997a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/Readme.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/Readme.md
new file mode 100644
index 0000000..af58e22
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/Readme.md
@@ -0,0 +1,342 @@
+# Commander.js
+
+
+[![Build Status](https://api.travis-ci.org/tj/commander.js.svg)](http://travis-ci.org/tj/commander.js)
+[![NPM Version](http://img.shields.io/npm/v/commander.svg?style=flat)](https://www.npmjs.org/package/commander)
+[![NPM Downloads](https://img.shields.io/npm/dm/commander.svg?style=flat)](https://www.npmjs.org/package/commander)
+[![Join the chat at https://gitter.im/tj/commander.js](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tj/commander.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+  The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/tj/commander).  
+  [API documentation](http://tj.github.com/commander.js/)
+
+
+## Installation
+
+    $ npm install commander
+
+## Option parsing
+
+ Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+  .version('0.0.1')
+  .option('-p, --peppers', 'Add peppers')
+  .option('-P, --pineapple', 'Add pineapple')
+  .option('-b, --bbq-sauce', 'Add bbq sauce')
+  .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
+  .parse(process.argv);
+
+console.log('you ordered a pizza with:');
+if (program.peppers) console.log('  - peppers');
+if (program.pineapple) console.log('  - pineapple');
+if (program.bbqSauce) console.log('  - bbq');
+console.log('  - %s cheese', program.cheese);
+```
+
+ Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc.
+
+
+## Coercion
+
+```js
+function range(val) {
+  return val.split('..').map(Number);
+}
+
+function list(val) {
+  return val.split(',');
+}
+
+function collect(val, memo) {
+  memo.push(val);
+  return memo;
+}
+
+function increaseVerbosity(v, total) {
+  return total + 1;
+}
+
+program
+  .version('0.0.1')
+  .usage('[options] <file ...>')
+  .option('-i, --integer <n>', 'An integer argument', parseInt)
+  .option('-f, --float <n>', 'A float argument', parseFloat)
+  .option('-r, --range <a>..<b>', 'A range', range)
+  .option('-l, --list <items>', 'A list', list)
+  .option('-o, --optional [value]', 'An optional value')
+  .option('-c, --collect [value]', 'A repeatable value', collect, [])
+  .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
+  .parse(process.argv);
+
+console.log(' int: %j', program.integer);
+console.log(' float: %j', program.float);
+console.log(' optional: %j', program.optional);
+program.range = program.range || [];
+console.log(' range: %j..%j', program.range[0], program.range[1]);
+console.log(' list: %j', program.list);
+console.log(' collect: %j', program.collect);
+console.log(' verbosity: %j', program.verbose);
+console.log(' args: %j', program.args);
+```
+
+## Regular Expression
+```js
+program
+  .version('0.0.1')
+  .option('-s --size <size>', 'Pizza size', /^(large|medium|small)$/i, 'medium')
+  .option('-d --drink [drink]', 'Drink', /^(coke|pepsi|izze)$/i)
+  .parse(process.argv);
+  
+console.log(' size: %j', program.size);
+console.log(' drink: %j', program.drink);
+```
+
+## Variadic arguments
+
+ The last argument of a command can be variadic, and only the last argument.  To make an argument variadic you have to
+ append `...` to the argument name.  Here is an example:
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+  .version('0.0.1')
+  .command('rmdir <dir> [otherDirs...]')
+  .action(function (dir, otherDirs) {
+    console.log('rmdir %s', dir);
+    if (otherDirs) {
+      otherDirs.forEach(function (oDir) {
+        console.log('rmdir %s', oDir);
+      });
+    }
+  });
+
+program.parse(process.argv);
+```
+
+ An `Array` is used for the value of a variadic argument.  This applies to `program.args` as well as the argument passed
+ to your action as demonstrated above.
+
+## Specify the argument syntax
+
+```js
+#!/usr/bin/env node
+
+var program = require('../');
+
+program
+  .version('0.0.1')
+  .arguments('<cmd> [env]')
+  .action(function (cmd, env) {
+     cmdValue = cmd;
+     envValue = env;
+  });
+
+program.parse(process.argv);
+
+if (typeof cmdValue === 'undefined') {
+   console.error('no command given!');
+   process.exit(1);
+}
+console.log('command:', cmdValue);
+console.log('environment:', envValue || "no environment given");
+```
+
+## Git-style sub-commands
+
+```js
+// file: ./examples/pm
+var program = require('..');
+
+program
+  .version('0.0.1')
+  .command('install [name]', 'install one or more packages')
+  .command('search [query]', 'search with optional query')
+  .command('list', 'list packages installed')
+  .parse(process.argv);
+```
+
+When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools.  
+The commander will try to search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-command`, like `pm-install`, `pm-search`.
+
+If the program is designed to be installed globally, make sure the executables have proper modes, like `755`.
+
+### `--harmony`
+
+You can enable `--harmony` option in two ways:
+* Use `#! /usr/bin/env node --harmony` in the sub-commands scripts. Note some os version don’t support this pattern.
+* Use the `--harmony` option when call the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning sub-command process.
+
+## Automated --help
+
+ The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:
+
+```  
+ $ ./examples/pizza --help
+
+   Usage: pizza [options]
+
+   An application for pizzas ordering
+
+   Options:
+
+     -h, --help           output usage information
+     -V, --version        output the version number
+     -p, --peppers        Add peppers
+     -P, --pineapple      Add pineapple
+     -b, --bbq            Add bbq sauce
+     -c, --cheese <type>  Add the specified type of cheese [marble]
+     -C, --no-cheese      You do not want any cheese
+
+```
+
+## Custom help
+
+ You can display arbitrary `-h, --help` information
+ by listening for "--help". Commander will automatically
+ exit once you are done so that the remainder of your program
+ does not execute causing undesired behaviours, for example
+ in the following executable "stuff" will not output when
+ `--help` is used.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+  .version('0.0.1')
+  .option('-f, --foo', 'enable some foo')
+  .option('-b, --bar', 'enable some bar')
+  .option('-B, --baz', 'enable some baz');
+
+// must be before .parse() since
+// node's emit() is immediate
+
+program.on('--help', function(){
+  console.log('  Examples:');
+  console.log('');
+  console.log('    $ custom-help --help');
+  console.log('    $ custom-help -h');
+  console.log('');
+});
+
+program.parse(process.argv);
+
+console.log('stuff');
+```
+
+Yields the following help output when `node script-name.js -h` or `node script-name.js --help` are run:
+
+```
+
+Usage: custom-help [options]
+
+Options:
+
+  -h, --help     output usage information
+  -V, --version  output the version number
+  -f, --foo      enable some foo
+  -b, --bar      enable some bar
+  -B, --baz      enable some baz
+
+Examples:
+
+  $ custom-help --help
+  $ custom-help -h
+
+```
+
+## .outputHelp()
+
+Output help information without exiting.
+
+If you want to display help by default (e.g. if no command was provided), you can use something like:
+
+```js
+var program = require('commander');
+
+program
+  .version('0.0.1')
+  .command('getstream [url]', 'get stream URL')
+  .parse(process.argv);
+
+  if (!process.argv.slice(2).length) {
+    program.outputHelp();
+  }
+```
+
+## .help()
+
+  Output help information and exit immediately.
+
+## Examples
+
+```js
+var program = require('commander');
+
+program
+  .version('0.0.1')
+  .option('-C, --chdir <path>', 'change the working directory')
+  .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+  .option('-T, --no-tests', 'ignore test hook')
+
+program
+  .command('setup [env]')
+  .description('run setup commands for all envs')
+  .option("-s, --setup_mode [mode]", "Which setup mode to use")
+  .action(function(env, options){
+    var mode = options.setup_mode || "normal";
+    env = env || 'all';
+    console.log('setup for %s env(s) with %s mode', env, mode);
+  });
+
+program
+  .command('exec <cmd>')
+  .alias('ex')
+  .description('execute the given remote cmd')
+  .option("-e, --exec_mode <mode>", "Which exec mode to use")
+  .action(function(cmd, options){
+    console.log('exec "%s" using %s mode', cmd, options.exec_mode);
+  }).on('--help', function() {
+    console.log('  Examples:');
+    console.log();
+    console.log('    $ deploy exec sequential');
+    console.log('    $ deploy exec async');
+    console.log();
+  });
+
+program
+  .command('*')
+  .action(function(env){
+    console.log('deploying "%s"', env);
+  });
+
+program.parse(process.argv);
+```
+
+More Demos can be found in the [examples](https://github.com/tj/commander.js/tree/master/examples) directory.
+
+## License
+
+MIT
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/index.js
new file mode 100644
index 0000000..8776965
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/index.js
@@ -0,0 +1,1103 @@
+
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter;
+var spawn = require('child_process').spawn;
+var readlink = require('graceful-readlink').readlinkSync;
+var path = require('path');
+var dirname = path.dirname;
+var basename = path.basename;
+var fs = require('fs');
+
+/**
+ * Expose the root command.
+ */
+
+exports = module.exports = new Command();
+
+/**
+ * Expose `Command`.
+ */
+
+exports.Command = Command;
+
+/**
+ * Expose `Option`.
+ */
+
+exports.Option = Option;
+
+/**
+ * Initialize a new `Option` with the given `flags` and `description`.
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @api public
+ */
+
+function Option(flags, description) {
+  this.flags = flags;
+  this.required = ~flags.indexOf('<');
+  this.optional = ~flags.indexOf('[');
+  this.bool = !~flags.indexOf('-no-');
+  flags = flags.split(/[ ,|]+/);
+  if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
+  this.long = flags.shift();
+  this.description = description || '';
+}
+
+/**
+ * Return option name.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Option.prototype.name = function() {
+  return this.long
+    .replace('--', '')
+    .replace('no-', '');
+};
+
+/**
+ * Check if `arg` matches the short or long flag.
+ *
+ * @param {String} arg
+ * @return {Boolean}
+ * @api private
+ */
+
+Option.prototype.is = function(arg) {
+  return arg == this.short || arg == this.long;
+};
+
+/**
+ * Initialize a new `Command`.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+function Command(name) {
+  this.commands = [];
+  this.options = [];
+  this._execs = [];
+  this._allowUnknownOption = false;
+  this._args = [];
+  this._name = name;
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+Command.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Add command `name`.
+ *
+ * The `.action()` callback is invoked when the
+ * command `name` is specified via __ARGV__,
+ * and the remaining arguments are applied to the
+ * function for access.
+ *
+ * When the `name` is "*" an un-matched command
+ * will be passed as the first arg, followed by
+ * the rest of __ARGV__ remaining.
+ *
+ * Examples:
+ *
+ *      program
+ *        .version('0.0.1')
+ *        .option('-C, --chdir <path>', 'change the working directory')
+ *        .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+ *        .option('-T, --no-tests', 'ignore test hook')
+ *
+ *      program
+ *        .command('setup')
+ *        .description('run remote setup commands')
+ *        .action(function() {
+ *          console.log('setup');
+ *        });
+ *
+ *      program
+ *        .command('exec <cmd>')
+ *        .description('run the given remote command')
+ *        .action(function(cmd) {
+ *          console.log('exec "%s"', cmd);
+ *        });
+ *
+ *      program
+ *        .command('teardown <dir> [otherDirs...]')
+ *        .description('run teardown commands')
+ *        .action(function(dir, otherDirs) {
+ *          console.log('dir "%s"', dir);
+ *          if (otherDirs) {
+ *            otherDirs.forEach(function (oDir) {
+ *              console.log('dir "%s"', oDir);
+ *            });
+ *          }
+ *        });
+ *
+ *      program
+ *        .command('*')
+ *        .description('deploy the given env')
+ *        .action(function(env) {
+ *          console.log('deploying "%s"', env);
+ *        });
+ *
+ *      program.parse(process.argv);
+  *
+ * @param {String} name
+ * @param {String} [desc] for git-style sub-commands
+ * @return {Command} the new command
+ * @api public
+ */
+
+Command.prototype.command = function(name, desc, opts) {
+  opts = opts || {};
+  var args = name.split(/ +/);
+  var cmd = new Command(args.shift());
+
+  if (desc) {
+    cmd.description(desc);
+    this.executables = true;
+    this._execs[cmd._name] = true;
+  }
+
+  cmd._noHelp = !!opts.noHelp;
+  this.commands.push(cmd);
+  cmd.parseExpectedArgs(args);
+  cmd.parent = this;
+
+  if (desc) return this;
+  return cmd;
+};
+
+/**
+ * Define argument syntax for the top-level command.
+ *
+ * @api public
+ */
+
+Command.prototype.arguments = function (desc) {
+  return this.parseExpectedArgs(desc.split(/ +/));
+}
+
+/**
+ * Add an implicit `help [cmd]` subcommand
+ * which invokes `--help` for the given command.
+ *
+ * @api private
+ */
+
+Command.prototype.addImplicitHelpCommand = function() {
+  this.command('help [cmd]', 'display help for [cmd]');
+};
+
+/**
+ * Parse expected `args`.
+ *
+ * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parseExpectedArgs = function(args) {
+  if (!args.length) return;
+  var self = this;
+  args.forEach(function(arg) {
+    var argDetails = {
+      required: false,
+      name: '',
+      variadic: false
+    };
+
+    switch (arg[0]) {
+      case '<':
+        argDetails.required = true;
+        argDetails.name = arg.slice(1, -1);
+        break;
+      case '[':
+        argDetails.name = arg.slice(1, -1);
+        break;
+    }
+
+    if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {
+      argDetails.variadic = true;
+      argDetails.name = argDetails.name.slice(0, -3);
+    }
+    if (argDetails.name) {
+      self._args.push(argDetails);
+    }
+  });
+  return this;
+};
+
+/**
+ * Register callback `fn` for the command.
+ *
+ * Examples:
+ *
+ *      program
+ *        .command('help')
+ *        .description('display verbose help')
+ *        .action(function() {
+ *           // output help here
+ *        });
+ *
+ * @param {Function} fn
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.action = function(fn) {
+  var self = this;
+  var listener = function(args, unknown) {
+    // Parse any so-far unknown options
+    args = args || [];
+    unknown = unknown || [];
+
+    var parsed = self.parseOptions(unknown);
+
+    // Output help if necessary
+    outputHelpIfNecessary(self, parsed.unknown);
+
+    // If there are still any unknown options, then we simply
+    // die, unless someone asked for help, in which case we give it
+    // to them, and then we die.
+    if (parsed.unknown.length > 0) {
+      self.unknownOption(parsed.unknown[0]);
+    }
+
+    // Leftover arguments need to be pushed back. Fixes issue #56
+    if (parsed.args.length) args = parsed.args.concat(args);
+
+    self._args.forEach(function(arg, i) {
+      if (arg.required && null == args[i]) {
+        self.missingArgument(arg.name);
+      } else if (arg.variadic) {
+        if (i !== self._args.length - 1) {
+          self.variadicArgNotLast(arg.name);
+        }
+
+        args[i] = args.splice(i);
+      }
+    });
+
+    // Always append ourselves to the end of the arguments,
+    // to make sure we match the number of arguments the user
+    // expects
+    if (self._args.length) {
+      args[self._args.length] = self;
+    } else {
+      args.push(self);
+    }
+
+    fn.apply(self, args);
+  };
+  var parent = this.parent || this;
+  var name = parent === this ? '*' : this._name;
+  parent.on(name, listener);
+  if (this._alias) parent.on(this._alias, listener);
+  return this;
+};
+
+/**
+ * Define option with `flags`, `description` and optional
+ * coercion `fn`.
+ *
+ * The `flags` string should contain both the short and long flags,
+ * separated by comma, a pipe or space. The following are all valid
+ * all will output this way when `--help` is used.
+ *
+ *    "-p, --pepper"
+ *    "-p|--pepper"
+ *    "-p --pepper"
+ *
+ * Examples:
+ *
+ *     // simple boolean defaulting to false
+ *     program.option('-p, --pepper', 'add pepper');
+ *
+ *     --pepper
+ *     program.pepper
+ *     // => Boolean
+ *
+ *     // simple boolean defaulting to true
+ *     program.option('-C, --no-cheese', 'remove cheese');
+ *
+ *     program.cheese
+ *     // => true
+ *
+ *     --no-cheese
+ *     program.cheese
+ *     // => false
+ *
+ *     // required argument
+ *     program.option('-C, --chdir <path>', 'change the working directory');
+ *
+ *     --chdir /tmp
+ *     program.chdir
+ *     // => "/tmp"
+ *
+ *     // optional argument
+ *     program.option('-c, --cheese [type]', 'add cheese [marble]');
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @param {Function|Mixed} fn or default
+ * @param {Mixed} defaultValue
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.option = function(flags, description, fn, defaultValue) {
+  var self = this
+    , option = new Option(flags, description)
+    , oname = option.name()
+    , name = camelcase(oname);
+
+  // default as 3rd arg
+  if (typeof fn != 'function') {
+    if (fn instanceof RegExp) {
+      var regex = fn;
+      fn = function(val, def) {
+        var m = regex.exec(val);
+        return m ? m[0] : def;
+      }
+    }
+    else {
+      defaultValue = fn;
+      fn = null;
+    }
+  }
+
+  // preassign default value only for --no-*, [optional], or <required>
+  if (false == option.bool || option.optional || option.required) {
+    // when --no-* we make sure default is true
+    if (false == option.bool) defaultValue = true;
+    // preassign only if we have a default
+    if (undefined !== defaultValue) self[name] = defaultValue;
+  }
+
+  // register the option
+  this.options.push(option);
+
+  // when it's passed assign the value
+  // and conditionally invoke the callback
+  this.on(oname, function(val) {
+    // coercion
+    if (null !== val && fn) val = fn(val, undefined === self[name]
+      ? defaultValue
+      : self[name]);
+
+    // unassigned or bool
+    if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
+      // if no value, bool true, and we have a default, then use it!
+      if (null == val) {
+        self[name] = option.bool
+          ? defaultValue || true
+          : false;
+      } else {
+        self[name] = val;
+      }
+    } else if (null !== val) {
+      // reassign
+      self[name] = val;
+    }
+  });
+
+  return this;
+};
+
+/**
+ * Allow unknown options on the command line.
+ *
+ * @param {Boolean} arg if `true` or omitted, no error will be thrown
+ * for unknown options.
+ * @api public
+ */
+Command.prototype.allowUnknownOption = function(arg) {
+    this._allowUnknownOption = arguments.length === 0 || arg;
+    return this;
+};
+
+/**
+ * Parse `argv`, settings options and invoking commands when defined.
+ *
+ * @param {Array} argv
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parse = function(argv) {
+  // implicit help
+  if (this.executables) this.addImplicitHelpCommand();
+
+  // store raw args
+  this.rawArgs = argv;
+
+  // guess name
+  this._name = this._name || basename(argv[1], '.js');
+
+  // github-style sub-commands with no sub-command
+  if (this.executables && argv.length < 3) {
+    // this user needs help
+    argv.push('--help');
+  }
+
+  // process argv
+  var parsed = this.parseOptions(this.normalize(argv.slice(2)));
+  var args = this.args = parsed.args;
+
+  var result = this.parseArgs(this.args, parsed.unknown);
+
+  // executable sub-commands
+  var name = result.args[0];
+  if (this._execs[name] && typeof this._execs[name] != "function") {
+    return this.executeSubCommand(argv, args, parsed.unknown);
+  }
+
+  return result;
+};
+
+/**
+ * Execute a sub-command executable.
+ *
+ * @param {Array} argv
+ * @param {Array} args
+ * @param {Array} unknown
+ * @api private
+ */
+
+Command.prototype.executeSubCommand = function(argv, args, unknown) {
+  args = args.concat(unknown);
+
+  if (!args.length) this.help();
+  if ('help' == args[0] && 1 == args.length) this.help();
+
+  // <cmd> --help
+  if ('help' == args[0]) {
+    args[0] = args[1];
+    args[1] = '--help';
+  }
+
+  // executable
+  var f = argv[1];
+  // name of the subcommand, link `pm-install`
+  var bin = basename(f, '.js') + '-' + args[0];
+
+
+  // In case of globally installed, get the base dir where executable
+  //  subcommand file should be located at
+  var baseDir
+    , link = readlink(f);
+
+  // when symbolink is relative path
+  if (link !== f && link.charAt(0) !== '/') {
+    link = path.join(dirname(f), link)
+  }
+  baseDir = dirname(link);
+
+  // prefer local `./<bin>` to bin in the $PATH
+  var localBin = path.join(baseDir, bin);
+
+  // whether bin file is a js script with explicit `.js` extension
+  var isExplicitJS = false;
+  if (exists(localBin + '.js')) {
+    bin = localBin + '.js';
+    isExplicitJS = true;
+  } else if (exists(localBin)) {
+    bin = localBin;
+  }
+
+  args = args.slice(1);
+
+  var proc;
+  if (process.platform !== 'win32') {
+    if (isExplicitJS) {
+      args.unshift(localBin);
+      // add executable arguments to spawn
+      args = (process.execArgv || []).concat(args);
+
+      proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
+    } else {
+      proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
+    }
+  } else {
+    args.unshift(localBin);
+    proc = spawn(process.execPath, args, { stdio: 'inherit'});
+  }
+
+  proc.on('close', process.exit.bind(process));
+  proc.on('error', function(err) {
+    if (err.code == "ENOENT") {
+      console.error('\n  %s(1) does not exist, try --help\n', bin);
+    } else if (err.code == "EACCES") {
+      console.error('\n  %s(1) not executable. try chmod or run with root\n', bin);
+    }
+    process.exit(1);
+  });
+
+  this.runningCommand = proc;
+};
+
+/**
+ * Normalize `args`, splitting joined short flags. For example
+ * the arg "-abc" is equivalent to "-a -b -c".
+ * This also normalizes equal sign and splits "--abc=def" into "--abc def".
+ *
+ * @param {Array} args
+ * @return {Array}
+ * @api private
+ */
+
+Command.prototype.normalize = function(args) {
+  var ret = []
+    , arg
+    , lastOpt
+    , index;
+
+  for (var i = 0, len = args.length; i < len; ++i) {
+    arg = args[i];
+    if (i > 0) {
+      lastOpt = this.optionFor(args[i-1]);
+    }
+
+    if (arg === '--') {
+      // Honor option terminator
+      ret = ret.concat(args.slice(i));
+      break;
+    } else if (lastOpt && lastOpt.required) {
+      ret.push(arg);
+    } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
+      arg.slice(1).split('').forEach(function(c) {
+        ret.push('-' + c);
+      });
+    } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
+      ret.push(arg.slice(0, index), arg.slice(index + 1));
+    } else {
+      ret.push(arg);
+    }
+  }
+
+  return ret;
+};
+
+/**
+ * Parse command `args`.
+ *
+ * When listener(s) are available those
+ * callbacks are invoked, otherwise the "*"
+ * event is emitted and those actions are invoked.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api private
+ */
+
+Command.prototype.parseArgs = function(args, unknown) {
+  var name;
+
+  if (args.length) {
+    name = args[0];
+    if (this.listeners(name).length) {
+      this.emit(args.shift(), args, unknown);
+    } else {
+      this.emit('*', args);
+    }
+  } else {
+    outputHelpIfNecessary(this, unknown);
+
+    // If there were no args and we have unknown options,
+    // then they are extraneous and we need to error.
+    if (unknown.length > 0) {
+      this.unknownOption(unknown[0]);
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Return an option matching `arg` if any.
+ *
+ * @param {String} arg
+ * @return {Option}
+ * @api private
+ */
+
+Command.prototype.optionFor = function(arg) {
+  for (var i = 0, len = this.options.length; i < len; ++i) {
+    if (this.options[i].is(arg)) {
+      return this.options[i];
+    }
+  }
+};
+
+/**
+ * Parse options from `argv` returning `argv`
+ * void of these options.
+ *
+ * @param {Array} argv
+ * @return {Array}
+ * @api public
+ */
+
+Command.prototype.parseOptions = function(argv) {
+  var args = []
+    , len = argv.length
+    , literal
+    , option
+    , arg;
+
+  var unknownOptions = [];
+
+  // parse options
+  for (var i = 0; i < len; ++i) {
+    arg = argv[i];
+
+    // literal args after --
+    if ('--' == arg) {
+      literal = true;
+      continue;
+    }
+
+    if (literal) {
+      args.push(arg);
+      continue;
+    }
+
+    // find matching Option
+    option = this.optionFor(arg);
+
+    // option is defined
+    if (option) {
+      // requires arg
+      if (option.required) {
+        arg = argv[++i];
+        if (null == arg) return this.optionMissingArgument(option);
+        this.emit(option.name(), arg);
+      // optional arg
+      } else if (option.optional) {
+        arg = argv[i+1];
+        if (null == arg || ('-' == arg[0] && '-' != arg)) {
+          arg = null;
+        } else {
+          ++i;
+        }
+        this.emit(option.name(), arg);
+      // bool
+      } else {
+        this.emit(option.name());
+      }
+      continue;
+    }
+
+    // looks like an option
+    if (arg.length > 1 && '-' == arg[0]) {
+      unknownOptions.push(arg);
+
+      // If the next argument looks like it might be
+      // an argument for this option, we pass it on.
+      // If it isn't, then it'll simply be ignored
+      if (argv[i+1] && '-' != argv[i+1][0]) {
+        unknownOptions.push(argv[++i]);
+      }
+      continue;
+    }
+
+    // arg
+    args.push(arg);
+  }
+
+  return { args: args, unknown: unknownOptions };
+};
+
+/**
+ * Return an object containing options as key-value pairs
+ *
+ * @return {Object}
+ * @api public
+ */
+Command.prototype.opts = function() {
+  var result = {}
+    , len = this.options.length;
+
+  for (var i = 0 ; i < len; i++) {
+    var key = camelcase(this.options[i].name());
+    result[key] = key === 'version' ? this._version : this[key];
+  }
+  return result;
+};
+
+/**
+ * Argument `name` is missing.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.missingArgument = function(name) {
+  console.error();
+  console.error("  error: missing required argument `%s'", name);
+  console.error();
+  process.exit(1);
+};
+
+/**
+ * `Option` is missing an argument, but received `flag` or nothing.
+ *
+ * @param {String} option
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.optionMissingArgument = function(option, flag) {
+  console.error();
+  if (flag) {
+    console.error("  error: option `%s' argument missing, got `%s'", option.flags, flag);
+  } else {
+    console.error("  error: option `%s' argument missing", option.flags);
+  }
+  console.error();
+  process.exit(1);
+};
+
+/**
+ * Unknown option `flag`.
+ *
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.unknownOption = function(flag) {
+  if (this._allowUnknownOption) return;
+  console.error();
+  console.error("  error: unknown option `%s'", flag);
+  console.error();
+  process.exit(1);
+};
+
+/**
+ * Variadic argument with `name` is not the last argument as required.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.variadicArgNotLast = function(name) {
+  console.error();
+  console.error("  error: variadic arguments must be last `%s'", name);
+  console.error();
+  process.exit(1);
+};
+
+/**
+ * Set the program version to `str`.
+ *
+ * This method auto-registers the "-V, --version" flag
+ * which will print the version number when passed.
+ *
+ * @param {String} str
+ * @param {String} flags
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.version = function(str, flags) {
+  if (0 == arguments.length) return this._version;
+  this._version = str;
+  flags = flags || '-V, --version';
+  this.option(flags, 'output the version number');
+  this.on('version', function() {
+    process.stdout.write(str + '\n');
+    process.exit(0);
+  });
+  return this;
+};
+
+/**
+ * Set the description to `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.description = function(str) {
+  if (0 == arguments.length) return this._description;
+  this._description = str;
+  return this;
+};
+
+/**
+ * Set an alias for the command
+ *
+ * @param {String} alias
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.alias = function(alias) {
+  if (0 == arguments.length) return this._alias;
+  this._alias = alias;
+  return this;
+};
+
+/**
+ * Set / get the command usage `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.usage = function(str) {
+  var args = this._args.map(function(arg) {
+    return humanReadableArgName(arg);
+  });
+
+  var usage = '[options]'
+    + (this.commands.length ? ' [command]' : '')
+    + (this._args.length ? ' ' + args.join(' ') : '');
+
+  if (0 == arguments.length) return this._usage || usage;
+  this._usage = str;
+
+  return this;
+};
+
+/**
+ * Get the name of the command
+ *
+ * @param {String} name
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.name = function() {
+  return this._name;
+};
+
+/**
+ * Return the largest option length.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+Command.prototype.largestOptionLength = function() {
+  return this.options.reduce(function(max, option) {
+    return Math.max(max, option.flags.length);
+  }, 0);
+};
+
+/**
+ * Return help for options.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.optionHelp = function() {
+  var width = this.largestOptionLength();
+
+  // Prepend the help information
+  return [pad('-h, --help', width) + '  ' + 'output usage information']
+    .concat(this.options.map(function(option) {
+      return pad(option.flags, width) + '  ' + option.description;
+      }))
+    .join('\n');
+};
+
+/**
+ * Return command help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.commandHelp = function() {
+  if (!this.commands.length) return '';
+
+  var commands = this.commands.filter(function(cmd) {
+    return !cmd._noHelp;
+  }).map(function(cmd) {
+    var args = cmd._args.map(function(arg) {
+      return humanReadableArgName(arg);
+    }).join(' ');
+
+    return [
+      cmd._name
+        + (cmd._alias
+          ? '|' + cmd._alias
+          : '')
+        + (cmd.options.length
+          ? ' [options]'
+          : '')
+        + ' ' + args
+    , cmd.description()
+    ];
+  });
+
+  var width = commands.reduce(function(max, command) {
+    return Math.max(max, command[0].length);
+  }, 0);
+
+  return [
+      ''
+    , '  Commands:'
+    , ''
+    , commands.map(function(cmd) {
+      return pad(cmd[0], width) + '  ' + cmd[1];
+    }).join('\n').replace(/^/gm, '    ')
+    , ''
+  ].join('\n');
+};
+
+/**
+ * Return program help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.helpInformation = function() {
+  var desc = [];
+  if (this._description) {
+    desc = [
+      '  ' + this._description
+      , ''
+    ];
+  }
+
+  var cmdName = this._name;
+  if (this._alias) {
+    cmdName = cmdName + '|' + this._alias;
+  }
+  var usage = [
+    ''
+    ,'  Usage: ' + cmdName + ' ' + this.usage()
+    , ''
+  ];
+
+  var cmds = [];
+  var commandHelp = this.commandHelp();
+  if (commandHelp) cmds = [commandHelp];
+
+  var options = [
+    '  Options:'
+    , ''
+    , '' + this.optionHelp().replace(/^/gm, '    ')
+    , ''
+    , ''
+  ];
+
+  return usage
+    .concat(cmds)
+    .concat(desc)
+    .concat(options)
+    .join('\n');
+};
+
+/**
+ * Output help information for this command
+ *
+ * @api public
+ */
+
+Command.prototype.outputHelp = function() {
+  process.stdout.write(this.helpInformation());
+  this.emit('--help');
+};
+
+/**
+ * Output help information and exit.
+ *
+ * @api public
+ */
+
+Command.prototype.help = function() {
+  this.outputHelp();
+  process.exit();
+};
+
+/**
+ * Camel-case the given `flag`
+ *
+ * @param {String} flag
+ * @return {String}
+ * @api private
+ */
+
+function camelcase(flag) {
+  return flag.split('-').reduce(function(str, word) {
+    return str + word[0].toUpperCase() + word.slice(1);
+  });
+}
+
+/**
+ * Pad `str` to `width`.
+ *
+ * @param {String} str
+ * @param {Number} width
+ * @return {String}
+ * @api private
+ */
+
+function pad(str, width) {
+  var len = Math.max(0, width - str.length);
+  return str + Array(len + 1).join(' ');
+}
+
+/**
+ * Output help information if necessary
+ *
+ * @param {Command} command to output help for
+ * @param {Array} array of options to search for -h or --help
+ * @api private
+ */
+
+function outputHelpIfNecessary(cmd, options) {
+  options = options || [];
+  for (var i = 0; i < options.length; i++) {
+    if (options[i] == '--help' || options[i] == '-h') {
+      cmd.outputHelp();
+      process.exit(0);
+    }
+  }
+}
+
+/**
+ * Takes an argument an returns its human readable equivalent for help usage.
+ *
+ * @param {Object} arg
+ * @return {String}
+ * @api private
+ */
+
+function humanReadableArgName(arg) {
+  var nameOutput = arg.name + (arg.variadic === true ? '...' : '');
+
+  return arg.required
+    ? '<' + nameOutput + '>'
+    : '[' + nameOutput + ']'
+}
+
+// for versions before node v0.8 when there weren't `fs.existsSync`
+function exists(file) {
+  try {
+    if (fs.statSync(file).isFile()) {
+      return true;
+    }
+  } catch (e) {
+    return false;
+  }
+}
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/.npmignore
new file mode 100644
index 0000000..3ac7d16
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/.npmignore
@@ -0,0 +1,3 @@
+.idea/
+.DS_Store
+node_modules/
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/.travis.yml
new file mode 100644
index 0000000..baf9be7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+  - "0.10"
+  - "0.12"
+  - "io.js"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/LICENSE
new file mode 100644
index 0000000..d1f842f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Zhiye Li
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/README.md
new file mode 100644
index 0000000..fc63b50
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/README.md
@@ -0,0 +1,17 @@
+# graceful-readlink
+[![NPM Version](http://img.shields.io/npm/v/graceful-readlink.svg?style=flat)](https://www.npmjs.org/package/graceful-readlink)
+[![NPM Downloads](https://img.shields.io/npm/dm/graceful-readlink.svg?style=flat)](https://www.npmjs.org/package/graceful-readlink)
+
+
+## Usage
+
+```js
+var readlinkSync = require('graceful-readlink').readlinkSync;
+console.log(readlinkSync(f));
+// output
+//  the file pointed to when `f` is a symbolic link
+//  the `f` itself when `f` is not a symbolic link
+```
+## Licence
+
+MIT License
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/index.js
new file mode 100644
index 0000000..7e9fc70
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/index.js
@@ -0,0 +1,12 @@
+var fs = require('fs')
+  , lstat = fs.lstatSync;
+
+exports.readlinkSync = function (p) {
+  if (lstat(p).isSymbolicLink()) {
+    return fs.readlinkSync(p);
+  } else {
+    return p;
+  }
+};
+
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/package.json
new file mode 100644
index 0000000..5b8486a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/node_modules/graceful-readlink/package.json
@@ -0,0 +1,48 @@
+{
+  "name": "graceful-readlink",
+  "version": "1.0.1",
+  "description": "graceful fs.readlink",
+  "main": "index.js",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/zhiyelee/graceful-readlink.git"
+  },
+  "homepage": "https://github.com/zhiyelee/graceful-readlink",
+  "bugs": {
+    "url": "https://github.com/zhiyelee/graceful-readlink/issues"
+  },
+  "keywords": [
+    "fs.readlink",
+    "readlink"
+  ],
+  "author": {
+    "name": "zhiyelee"
+  },
+  "license": "MIT",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "gitHead": "f6655275bebef706fb63fd01b5f062a7052419a5",
+  "_id": "graceful-readlink@1.0.1",
+  "_shasum": "4cafad76bc62f02fa039b2f94e9a3dd3a391a725",
+  "_from": "graceful-readlink@>=1.0.0",
+  "_npmVersion": "2.1.17",
+  "_nodeVersion": "0.11.14",
+  "_npmUser": {
+    "name": "zhiyelee",
+    "email": "zhiyelee@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "zhiyelee",
+      "email": "zhiyelee@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "4cafad76bc62f02fa039b2f94e9a3dd3a391a725",
+    "tarball": "http://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/package.json
new file mode 100644
index 0000000..d01b575
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/commander/package.json
@@ -0,0 +1,75 @@
+{
+  "name": "commander",
+  "version": "2.8.1",
+  "description": "the complete solution for node.js command-line programs",
+  "keywords": [
+    "command",
+    "option",
+    "parser"
+  ],
+  "author": {
+    "name": "TJ Holowaychuk",
+    "email": "tj@vision-media.ca"
+  },
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/tj/commander.js.git"
+  },
+  "devDependencies": {
+    "should": ">= 0.0.1",
+    "sinon": ">= 1.14.1"
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "main": "index",
+  "engines": {
+    "node": ">= 0.6.x"
+  },
+  "files": [
+    "index.js"
+  ],
+  "dependencies": {
+    "graceful-readlink": ">= 1.0.0"
+  },
+  "gitHead": "c6c84726050b19c0373de27cd359f3baddff579f",
+  "bugs": {
+    "url": "https://github.com/tj/commander.js/issues"
+  },
+  "homepage": "https://github.com/tj/commander.js",
+  "_id": "commander@2.8.1",
+  "_shasum": "06be367febfda0c330aa1e2a072d3dc9762425d4",
+  "_from": "commander@>=2.8.0 <2.9.0",
+  "_npmVersion": "2.5.1",
+  "_nodeVersion": "0.12.0",
+  "_npmUser": {
+    "name": "zhiyelee",
+    "email": "zhiyelee@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "tjholowaychuk",
+      "email": "tj@vision-media.ca"
+    },
+    {
+      "name": "somekittens",
+      "email": "rkoutnik@gmail.com"
+    },
+    {
+      "name": "zhiyelee",
+      "email": "zhiyelee@gmail.com"
+    },
+    {
+      "name": "thethomaseffect",
+      "email": "thethomaseffect@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "06be367febfda0c330aa1e2a072d3dc9762425d4",
+    "tarball": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/README.md
new file mode 100644
index 0000000..b7c6786
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/README.md
@@ -0,0 +1,510 @@
+# Source Map
+
+This is a library to generate and consume the source map format
+[described here][format].
+
+This library is written in the Asynchronous Module Definition format, and works
+in the following environments:
+
+* Modern Browsers supporting ECMAScript 5 (either after the build, or with an
+  AMD loader such as RequireJS)
+
+* Inside Firefox (as a JSM file, after the build)
+
+* With NodeJS versions 0.8.X and higher
+
+## Node
+
+    $ npm install source-map
+
+## Building from Source (for everywhere else)
+
+Install Node and then run
+
+    $ git clone https://fitzgen@github.com/mozilla/source-map.git
+    $ cd source-map
+    $ npm link .
+
+Next, run
+
+    $ node Makefile.dryice.js
+
+This should spew a bunch of stuff to stdout, and create the following files:
+
+* `dist/source-map.js` - The unminified browser version.
+
+* `dist/source-map.min.js` - The minified browser version.
+
+* `dist/SourceMap.jsm` - The JavaScript Module for inclusion in Firefox source.
+
+## Examples
+
+### Consuming a source map
+
+```js
+var rawSourceMap = {
+  version: 3,
+  file: 'min.js',
+  names: ['bar', 'baz', 'n'],
+  sources: ['one.js', 'two.js'],
+  sourceRoot: 'http://example.com/www/js/',
+  mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+};
+
+var smc = new SourceMapConsumer(rawSourceMap);
+
+console.log(smc.sources);
+// [ 'http://example.com/www/js/one.js',
+//   'http://example.com/www/js/two.js' ]
+
+console.log(smc.originalPositionFor({
+  line: 2,
+  column: 28
+}));
+// { source: 'http://example.com/www/js/two.js',
+//   line: 2,
+//   column: 10,
+//   name: 'n' }
+
+console.log(smc.generatedPositionFor({
+  source: 'http://example.com/www/js/two.js',
+  line: 2,
+  column: 10
+}));
+// { line: 2, column: 28 }
+
+smc.eachMapping(function (m) {
+  // ...
+});
+```
+
+### Generating a source map
+
+In depth guide:
+[**Compiling to JavaScript, and Debugging with Source Maps**](https://hacks.mozilla.org/2013/05/compiling-to-javascript-and-debugging-with-source-maps/)
+
+#### With SourceNode (high level API)
+
+```js
+function compile(ast) {
+  switch (ast.type) {
+  case 'BinaryExpression':
+    return new SourceNode(
+      ast.location.line,
+      ast.location.column,
+      ast.location.source,
+      [compile(ast.left), " + ", compile(ast.right)]
+    );
+  case 'Literal':
+    return new SourceNode(
+      ast.location.line,
+      ast.location.column,
+      ast.location.source,
+      String(ast.value)
+    );
+  // ...
+  default:
+    throw new Error("Bad AST");
+  }
+}
+
+var ast = parse("40 + 2", "add.js");
+console.log(compile(ast).toStringWithSourceMap({
+  file: 'add.js'
+}));
+// { code: '40 + 2',
+//   map: [object SourceMapGenerator] }
+```
+
+#### With SourceMapGenerator (low level API)
+
+```js
+var map = new SourceMapGenerator({
+  file: "source-mapped.js"
+});
+
+map.addMapping({
+  generated: {
+    line: 10,
+    column: 35
+  },
+  source: "foo.js",
+  original: {
+    line: 33,
+    column: 2
+  },
+  name: "christopher"
+});
+
+console.log(map.toString());
+// '{"version":3,"file":"source-mapped.js","sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'
+```
+
+## API
+
+Get a reference to the module:
+
+```js
+// NodeJS
+var sourceMap = require('source-map');
+
+// Browser builds
+var sourceMap = window.sourceMap;
+
+// Inside Firefox
+let sourceMap = {};
+Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+```
+
+### SourceMapConsumer
+
+A SourceMapConsumer instance represents a parsed source map which we can query
+for information about the original file positions by giving it a file position
+in the generated source.
+
+#### new SourceMapConsumer(rawSourceMap)
+
+The only parameter is the raw source map (either as a string which can be
+`JSON.parse`'d, or an object). According to the spec, source maps have the
+following attributes:
+
+* `version`: Which version of the source map spec this map is following.
+
+* `sources`: An array of URLs to the original source files.
+
+* `names`: An array of identifiers which can be referrenced by individual
+  mappings.
+
+* `sourceRoot`: Optional. The URL root from which all sources are relative.
+
+* `sourcesContent`: Optional. An array of contents of the original source files.
+
+* `mappings`: A string of base64 VLQs which contain the actual mappings.
+
+* `file`: Optional. The generated filename this source map is associated with.
+
+#### SourceMapConsumer.prototype.computeColumnSpans()
+
+Compute the last column for each generated mapping. The last column is
+inclusive.
+
+#### SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
+
+Returns the original source, line, and column information for the generated
+source's line and column positions provided. The only argument is an object with
+the following properties:
+
+* `line`: The line number in the generated source.
+
+* `column`: The column number in the generated source.
+
+* `bias`: Either `SourceMapConsumer.GREATEST_LOWER_BOUND` or
+  `SourceMapConsumer.LEAST_UPPER_BOUND`. Specifies whether to return the closest
+  element that is smaller than or greater than the one we are searching for,
+  respectively, if the exact element cannot be found.  Defaults to
+  `SourceMapConsumer.GREATEST_LOWER_BOUND`.
+
+and an object is returned with the following properties:
+
+* `source`: The original source file, or null if this information is not
+  available.
+
+* `line`: The line number in the original source, or null if this information is
+  not available.
+
+* `column`: The column number in the original source, or null or null if this
+  information is not available.
+
+* `name`: The original identifier, or null if this information is not available.
+
+#### SourceMapConsumer.prototype.generatedPositionFor(originalPosition)
+
+Returns the generated line and column information for the original source,
+line, and column positions provided. The only argument is an object with
+the following properties:
+
+* `source`: The filename of the original source.
+
+* `line`: The line number in the original source.
+
+* `column`: The column number in the original source.
+
+and an object is returned with the following properties:
+
+* `line`: The line number in the generated source, or null.
+
+* `column`: The column number in the generated source, or null.
+
+#### SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition)
+
+Returns all generated line and column information for the original source, line,
+and column provided. If no column is provided, returns all mappings
+corresponding to a either the line we are searching for or the next closest line
+that has any mappings. Otherwise, returns all mappings corresponding to the
+given line and either the column we are searching for or the next closest column
+that has any offsets.
+
+The only argument is an object with the following properties:
+
+* `source`: The filename of the original source.
+
+* `line`: The line number in the original source.
+
+* `column`: Optional. The column number in the original source.
+
+and an array of objects is returned, each with the following properties:
+
+* `line`: The line number in the generated source, or null.
+
+* `column`: The column number in the generated source, or null.
+
+#### SourceMapConsumer.prototype.hasContentsOfAllSources()
+
+Return true if we have the embedded source content for every source listed in
+the source map, false otherwise.
+
+In other words, if this method returns `true`, then `smc.sourceContentFor(s)`
+will succeed for every source `s` in `smc.sources`.
+
+#### SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing])
+
+Returns the original source content for the source provided. The only
+argument is the URL of the original source file.
+
+If the source content for the given source is not found, then an error is
+thrown. Optionally, pass `true` as the second param to have `null` returned
+instead.
+
+#### SourceMapConsumer.prototype.eachMapping(callback, context, order)
+
+Iterate over each mapping between an original source/line/column and a
+generated line/column in this source map.
+
+* `callback`: The function that is called with each mapping. Mappings have the
+  form `{ source, generatedLine, generatedColumn, originalLine, originalColumn,
+  name }`
+
+* `context`: Optional. If specified, this object will be the value of `this`
+  every time that `callback` is called.
+
+* `order`: Either `SourceMapConsumer.GENERATED_ORDER` or
+  `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to iterate over
+  the mappings sorted by the generated file's line/column order or the
+  original's source/line/column order, respectively. Defaults to
+  `SourceMapConsumer.GENERATED_ORDER`.
+
+### SourceMapGenerator
+
+An instance of the SourceMapGenerator represents a source map which is being
+built incrementally.
+
+#### new SourceMapGenerator([startOfSourceMap])
+
+You may pass an object with the following properties:
+
+* `file`: The filename of the generated source that this source map is
+  associated with.
+
+* `sourceRoot`: A root for all relative URLs in this source map.
+
+* `skipValidation`: Optional. When `true`, disables validation of mappings as
+  they are added. This can improve performance but should be used with
+  discretion, as a last resort. Even then, one should avoid using this flag when
+  running tests, if possible.
+
+#### SourceMapGenerator.fromSourceMap(sourceMapConsumer)
+
+Creates a new SourceMapGenerator based on a SourceMapConsumer
+
+* `sourceMapConsumer` The SourceMap.
+
+#### SourceMapGenerator.prototype.addMapping(mapping)
+
+Add a single mapping from original source line and column to the generated
+source's line and column for this source map being created. The mapping object
+should have the following properties:
+
+* `generated`: An object with the generated line and column positions.
+
+* `original`: An object with the original line and column positions.
+
+* `source`: The original source file (relative to the sourceRoot).
+
+* `name`: An optional original token name for this mapping.
+
+#### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for an original source file.
+
+* `sourceFile` the URL of the original source file.
+
+* `sourceContent` the content of the source file.
+
+#### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])
+
+Applies a SourceMap for a source file to the SourceMap.
+Each mapping to the supplied source file is rewritten using the
+supplied SourceMap. Note: The resolution for the resulting mappings
+is the minimium of this map and the supplied map.
+
+* `sourceMapConsumer`: The SourceMap to be applied.
+
+* `sourceFile`: Optional. The filename of the source file.
+  If omitted, sourceMapConsumer.file will be used, if it exists.
+  Otherwise an error will be thrown.
+
+* `sourceMapPath`: Optional. The dirname of the path to the SourceMap
+  to be applied. If relative, it is relative to the SourceMap.
+
+  This parameter is needed when the two SourceMaps aren't in the same
+  directory, and the SourceMap to be applied contains relative source
+  paths. If so, those relative source paths need to be rewritten
+  relative to the SourceMap.
+
+  If omitted, it is assumed that both SourceMaps are in the same directory,
+  thus not needing any rewriting. (Supplying `'.'` has the same effect.)
+
+#### SourceMapGenerator.prototype.toString()
+
+Renders the source map being generated to a string.
+
+### SourceNode
+
+SourceNodes provide a way to abstract over interpolating and/or concatenating
+snippets of generated JavaScript source code, while maintaining the line and
+column information associated between those snippets and the original source
+code. This is useful as the final intermediate representation a compiler might
+use before outputting the generated JS and source map.
+
+#### new SourceNode([line, column, source[, chunk[, name]]])
+
+* `line`: The original line number associated with this source node, or null if
+  it isn't associated with an original line.
+
+* `column`: The original column number associated with this source node, or null
+  if it isn't associated with an original column.
+
+* `source`: The original source's filename; null if no filename is provided.
+
+* `chunk`: Optional. Is immediately passed to `SourceNode.prototype.add`, see
+  below.
+
+* `name`: Optional. The original identifier.
+
+#### SourceNode.fromStringWithSourceMap(code, sourceMapConsumer[, relativePath])
+
+Creates a SourceNode from generated code and a SourceMapConsumer.
+
+* `code`: The generated code
+
+* `sourceMapConsumer` The SourceMap for the generated code
+
+* `relativePath` The optional path that relative sources in `sourceMapConsumer`
+  should be relative to.
+
+#### SourceNode.prototype.add(chunk)
+
+Add a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+   `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.prepend(chunk)
+
+Prepend a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+   `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for a source file. This will be added to the
+`SourceMap` in the `sourcesContent` field.
+
+* `sourceFile`: The filename of the source file
+
+* `sourceContent`: The content of the source file
+
+#### SourceNode.prototype.walk(fn)
+
+Walk over the tree of JS snippets in this node and its children. The walking
+function is called once for each snippet of JS and is passed that snippet and
+the its original associated source's line/column location.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.walkSourceContents(fn)
+
+Walk over the tree of SourceNodes. The walking function is called for each
+source file content and is passed the filename and source content.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.join(sep)
+
+Like `Array.prototype.join` except for SourceNodes. Inserts the separator
+between each of this source node's children.
+
+* `sep`: The separator.
+
+#### SourceNode.prototype.replaceRight(pattern, replacement)
+
+Call `String.prototype.replace` on the very right-most source snippet. Useful
+for trimming whitespace from the end of a source node, etc.
+
+* `pattern`: The pattern to replace.
+
+* `replacement`: The thing to replace the pattern with.
+
+#### SourceNode.prototype.toString()
+
+Return the string representation of this source node. Walks over the tree and
+concatenates all the various snippets together to one string.
+
+#### SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])
+
+Returns the string representation of this tree of source nodes, plus a
+SourceMapGenerator which contains all the mappings between the generated and
+original sources.
+
+The arguments are the same as those to `new SourceMapGenerator`.
+
+## Tests
+
+[![Build Status](https://travis-ci.org/mozilla/source-map.png?branch=master)](https://travis-ci.org/mozilla/source-map)
+
+Install NodeJS version 0.8.0 or greater, then run `node test/run-tests.js`.
+
+To add new tests, create a new file named `test/test-<your new test name>.js`
+and export your test functions with names that start with "test", for example
+
+```js
+exports["test doing the foo bar"] = function (assert, util) {
+  ...
+};
+```
+
+The new test will be located automatically when you run the suite.
+
+The `util` argument is the test utility module located at `test/source-map/util`.
+
+The `assert` argument is a cut down version of node's assert module. You have
+access to the following assertion functions:
+
+* `doesNotThrow`
+
+* `equal`
+
+* `ok`
+
+* `strictEqual`
+
+* `throws`
+
+(The reason for the restricted set of test functions is because we need the
+tests to run inside Firefox's test suite as well and so the assert module is
+shimmed in that environment. See `build/assert-shim.js`.)
+
+[format]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+[feature]: https://wiki.mozilla.org/DevTools/Features/SourceMap
+[Dryice]: https://github.com/mozilla/dryice
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/assert-shim.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/assert-shim.js
new file mode 100644
index 0000000..daa1a62
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/assert-shim.js
@@ -0,0 +1,56 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('test/source-map/assert', ['exports'], function (exports) {
+
+  let do_throw = function (msg) {
+    throw new Error(msg);
+  };
+
+  exports.init = function (throw_fn) {
+    do_throw = throw_fn;
+  };
+
+  exports.doesNotThrow = function (fn) {
+    try {
+      fn();
+    }
+    catch (e) {
+      do_throw(e.message);
+    }
+  };
+
+  exports.equal = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' != ' + String(expected);
+    if (actual != expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.ok = function (val, msg) {
+    msg = msg || String(val) + ' is falsey';
+    if (!Boolean(val)) {
+      do_throw(msg);
+    }
+  };
+
+  exports.strictEqual = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' !== ' + String(expected);
+    if (actual !== expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.throws = function (fn) {
+    try {
+      fn();
+      do_throw('Expected an error to be thrown, but it wasn\'t.');
+    }
+    catch (e) {
+    }
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/mini-require.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/mini-require.js
new file mode 100644
index 0000000..0daf453
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/mini-require.js
@@ -0,0 +1,152 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/**
+ * Define a module along with a payload.
+ * @param {string} moduleName Name for the payload
+ * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
+ * @param {function} payload Function with (require, exports, module) params
+ */
+function define(moduleName, deps, payload) {
+  if (typeof moduleName != "string") {
+    throw new TypeError('Expected string, got: ' + moduleName);
+  }
+
+  if (arguments.length == 2) {
+    payload = deps;
+  }
+
+  if (moduleName in define.modules) {
+    throw new Error("Module already defined: " + moduleName);
+  }
+  define.modules[moduleName] = payload;
+};
+
+/**
+ * The global store of un-instantiated modules
+ */
+define.modules = {};
+
+
+/**
+ * We invoke require() in the context of a Domain so we can have multiple
+ * sets of modules running separate from each other.
+ * This contrasts with JSMs which are singletons, Domains allows us to
+ * optionally load a CommonJS module twice with separate data each time.
+ * Perhaps you want 2 command lines with a different set of commands in each,
+ * for example.
+ */
+function Domain() {
+  this.modules = {};
+  this._currentModule = null;
+}
+
+(function () {
+
+  /**
+   * Lookup module names and resolve them by calling the definition function if
+   * needed.
+   * There are 2 ways to call this, either with an array of dependencies and a
+   * callback to call when the dependencies are found (which can happen
+   * asynchronously in an in-page context) or with a single string an no callback
+   * where the dependency is resolved synchronously and returned.
+   * The API is designed to be compatible with the CommonJS AMD spec and
+   * RequireJS.
+   * @param {string[]|string} deps A name, or names for the payload
+   * @param {function|undefined} callback Function to call when the dependencies
+   * are resolved
+   * @return {undefined|object} The module required or undefined for
+   * array/callback method
+   */
+  Domain.prototype.require = function(deps, callback) {
+    if (Array.isArray(deps)) {
+      var params = deps.map(function(dep) {
+        return this.lookup(dep);
+      }, this);
+      if (callback) {
+        callback.apply(null, params);
+      }
+      return undefined;
+    }
+    else {
+      return this.lookup(deps);
+    }
+  };
+
+  function normalize(path) {
+    var bits = path.split('/');
+    var i = 1;
+    while (i < bits.length) {
+      if (bits[i] === '..') {
+        bits.splice(i-1, 1);
+      } else if (bits[i] === '.') {
+        bits.splice(i, 1);
+      } else {
+        i++;
+      }
+    }
+    return bits.join('/');
+  }
+
+  function join(a, b) {
+    a = a.trim();
+    b = b.trim();
+    if (/^\//.test(b)) {
+      return b;
+    } else {
+      return a.replace(/\/*$/, '/') + b;
+    }
+  }
+
+  function dirname(path) {
+    var bits = path.split('/');
+    bits.pop();
+    return bits.join('/');
+  }
+
+  /**
+   * Lookup module names and resolve them by calling the definition function if
+   * needed.
+   * @param {string} moduleName A name for the payload to lookup
+   * @return {object} The module specified by aModuleName or null if not found.
+   */
+  Domain.prototype.lookup = function(moduleName) {
+    if (/^\./.test(moduleName)) {
+      moduleName = normalize(join(dirname(this._currentModule), moduleName));
+    }
+
+    if (moduleName in this.modules) {
+      var module = this.modules[moduleName];
+      return module;
+    }
+
+    if (!(moduleName in define.modules)) {
+      throw new Error("Module not defined: " + moduleName);
+    }
+
+    var module = define.modules[moduleName];
+
+    if (typeof module == "function") {
+      var exports = {};
+      var previousModule = this._currentModule;
+      this._currentModule = moduleName;
+      module(this.require.bind(this), exports, { id: moduleName, uri: "" });
+      this._currentModule = previousModule;
+      module = exports;
+    }
+
+    // cache the resulting module object for next time
+    this.modules[moduleName] = module;
+
+    return module;
+  };
+
+}());
+
+define.Domain = Domain;
+define.globalDomain = new Domain();
+var require = define.globalDomain.require.bind(define.globalDomain);
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/prefix-source-map.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/prefix-source-map.jsm
new file mode 100644
index 0000000..209dbd7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/prefix-source-map.jsm
@@ -0,0 +1,21 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+this.EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
+
+Components.utils.import("resource://gre/modules/devtools/Console.jsm");
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/prefix-utils.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/prefix-utils.jsm
new file mode 100644
index 0000000..80341d4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/prefix-utils.jsm
@@ -0,0 +1,18 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
+Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
+
+this.EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-browser.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-browser.js
new file mode 100644
index 0000000..fb29ff5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-browser.js
@@ -0,0 +1,8 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.sourceMap = {
+  SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer,
+  SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator,
+  SourceNode: require('source-map/source-node').SourceNode
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-source-map.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-source-map.jsm
new file mode 100644
index 0000000..cf3c2d8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-source-map.jsm
@@ -0,0 +1,6 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+this.SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+this.SourceNode = require('source-map/source-node').SourceNode;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-utils.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-utils.jsm
new file mode 100644
index 0000000..b31b84c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/suffix-utils.jsm
@@ -0,0 +1,21 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+function runSourceMapTests(modName, do_throw) {
+  let mod = require(modName);
+  let assert = require('test/source-map/assert');
+  let util = require('test/source-map/util');
+
+  assert.init(do_throw);
+
+  for (let k in mod) {
+    if (/^test/.test(k)) {
+      mod[k](assert, util);
+    }
+  }
+
+}
+this.runSourceMapTests = runSourceMapTests;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/test-prefix.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/test-prefix.js
new file mode 100644
index 0000000..1b13f30
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/test-prefix.js
@@ -0,0 +1,8 @@
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://test/Utils.jsm');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/test-suffix.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/test-suffix.js
new file mode 100644
index 0000000..bec2de3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/build/test-suffix.js
@@ -0,0 +1,3 @@
+function run_test() {
+  runSourceMapTests('{THIS_MODULE}', do_throw);
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map.js
new file mode 100644
index 0000000..121ad24
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map.js
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
+exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
+exports.SourceNode = require('./source-map/source-node').SourceNode;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/array-set.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/array-set.js
new file mode 100644
index 0000000..19cb841
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/array-set.js
@@ -0,0 +1,107 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+
+  /**
+   * A data structure which is a combination of an array and a set. Adding a new
+   * member is O(1), testing for membership is O(1), and finding the index of an
+   * element is O(1). Removing elements from the set is not supported. Only
+   * strings are supported for membership.
+   */
+  function ArraySet() {
+    this._array = [];
+    this._set = {};
+  }
+
+  /**
+   * Static method for creating ArraySet instances from an existing array.
+   */
+  ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
+    var set = new ArraySet();
+    for (var i = 0, len = aArray.length; i < len; i++) {
+      set.add(aArray[i], aAllowDuplicates);
+    }
+    return set;
+  };
+
+  /**
+   * Return how many unique items are in this ArraySet. If duplicates have been
+   * added, than those do not count towards the size.
+   *
+   * @returns Number
+   */
+  ArraySet.prototype.size = function ArraySet_size() {
+    return Object.getOwnPropertyNames(this._set).length;
+  };
+
+  /**
+   * Add the given string to this set.
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
+    var isDuplicate = this.has(aStr);
+    var idx = this._array.length;
+    if (!isDuplicate || aAllowDuplicates) {
+      this._array.push(aStr);
+    }
+    if (!isDuplicate) {
+      this._set[util.toSetString(aStr)] = idx;
+    }
+  };
+
+  /**
+   * Is the given string a member of this set?
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.has = function ArraySet_has(aStr) {
+    return Object.prototype.hasOwnProperty.call(this._set,
+                                                util.toSetString(aStr));
+  };
+
+  /**
+   * What is the index of the given string in the array?
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+    if (this.has(aStr)) {
+      return this._set[util.toSetString(aStr)];
+    }
+    throw new Error('"' + aStr + '" is not in the set.');
+  };
+
+  /**
+   * What is the element at the given index?
+   *
+   * @param Number aIdx
+   */
+  ArraySet.prototype.at = function ArraySet_at(aIdx) {
+    if (aIdx >= 0 && aIdx < this._array.length) {
+      return this._array[aIdx];
+    }
+    throw new Error('No element indexed by ' + aIdx);
+  };
+
+  /**
+   * Returns the array representation of this set (which has the proper indices
+   * indicated by indexOf). Note that this is a copy of the internal array used
+   * for storing the members so that no one can mess with internal state.
+   */
+  ArraySet.prototype.toArray = function ArraySet_toArray() {
+    return this._array.slice();
+  };
+
+  exports.ArraySet = ArraySet;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/base64-vlq.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/base64-vlq.js
new file mode 100644
index 0000000..bbe9a58
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/base64-vlq.js
@@ -0,0 +1,146 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ *  * Neither the name of Google Inc. nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64 = require('./base64');
+
+  // A single base 64 digit can contain 6 bits of data. For the base 64 variable
+  // length quantities we use in the source map spec, the first bit is the sign,
+  // the next four bits are the actual value, and the 6th bit is the
+  // continuation bit. The continuation bit tells us whether there are more
+  // digits in this value following this digit.
+  //
+  //   Continuation
+  //   |    Sign
+  //   |    |
+  //   V    V
+  //   101011
+
+  var VLQ_BASE_SHIFT = 5;
+
+  // binary: 100000
+  var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+  // binary: 011111
+  var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+  // binary: 100000
+  var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+  /**
+   * Converts from a two-complement value to a value where the sign bit is
+   * placed in the least significant bit.  For example, as decimals:
+   *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+   *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+   */
+  function toVLQSigned(aValue) {
+    return aValue < 0
+      ? ((-aValue) << 1) + 1
+      : (aValue << 1) + 0;
+  }
+
+  /**
+   * Converts to a two-complement value from a value where the sign bit is
+   * placed in the least significant bit.  For example, as decimals:
+   *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+   *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+   */
+  function fromVLQSigned(aValue) {
+    var isNegative = (aValue & 1) === 1;
+    var shifted = aValue >> 1;
+    return isNegative
+      ? -shifted
+      : shifted;
+  }
+
+  /**
+   * Returns the base 64 VLQ encoded value.
+   */
+  exports.encode = function base64VLQ_encode(aValue) {
+    var encoded = "";
+    var digit;
+
+    var vlq = toVLQSigned(aValue);
+
+    do {
+      digit = vlq & VLQ_BASE_MASK;
+      vlq >>>= VLQ_BASE_SHIFT;
+      if (vlq > 0) {
+        // There are still more digits in this value, so we must make sure the
+        // continuation bit is marked.
+        digit |= VLQ_CONTINUATION_BIT;
+      }
+      encoded += base64.encode(digit);
+    } while (vlq > 0);
+
+    return encoded;
+  };
+
+  /**
+   * Decodes the next base 64 VLQ value from the given string and returns the
+   * value and the rest of the string via the out parameter.
+   */
+  exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) {
+    var strLen = aStr.length;
+    var result = 0;
+    var shift = 0;
+    var continuation, digit;
+
+    do {
+      if (aIndex >= strLen) {
+        throw new Error("Expected more digits in base 64 VLQ value.");
+      }
+
+      digit = base64.decode(aStr.charCodeAt(aIndex++));
+      if (digit === -1) {
+        throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1));
+      }
+
+      continuation = !!(digit & VLQ_CONTINUATION_BIT);
+      digit &= VLQ_BASE_MASK;
+      result = result + (digit << shift);
+      shift += VLQ_BASE_SHIFT;
+    } while (continuation);
+
+    aOutParam.value = fromVLQSigned(result);
+    aOutParam.rest = aIndex;
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/base64.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/base64.js
new file mode 100644
index 0000000..35adbc1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/base64.js
@@ -0,0 +1,73 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
+
+  /**
+   * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+   */
+  exports.encode = function (number) {
+    if (0 <= number && number < intToCharMap.length) {
+      return intToCharMap[number];
+    }
+    throw new TypeError("Must be between 0 and 63: " + aNumber);
+  };
+
+  /**
+   * Decode a single base 64 character code digit to an integer. Returns -1 on
+   * failure.
+   */
+  exports.decode = function (charCode) {
+    var bigA = 65;     // 'A'
+    var bigZ = 90;     // 'Z'
+
+    var littleA = 97;  // 'a'
+    var littleZ = 122; // 'z'
+
+    var zero = 48;     // '0'
+    var nine = 57;     // '9'
+
+    var plus = 43;     // '+'
+    var slash = 47;    // '/'
+
+    var littleOffset = 26;
+    var numberOffset = 52;
+
+    // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ
+    if (bigA <= charCode && charCode <= bigZ) {
+      return (charCode - bigA);
+    }
+
+    // 26 - 51: abcdefghijklmnopqrstuvwxyz
+    if (littleA <= charCode && charCode <= littleZ) {
+      return (charCode - littleA + littleOffset);
+    }
+
+    // 52 - 61: 0123456789
+    if (zero <= charCode && charCode <= nine) {
+      return (charCode - zero + numberOffset);
+    }
+
+    // 62: +
+    if (charCode == plus) {
+      return 62;
+    }
+
+    // 63: /
+    if (charCode == slash) {
+      return 63;
+    }
+
+    // Invalid base64 digit.
+    return -1;
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/binary-search.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/binary-search.js
new file mode 100644
index 0000000..7936f7e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/binary-search.js
@@ -0,0 +1,117 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  exports.GREATEST_LOWER_BOUND = 1;
+  exports.LEAST_UPPER_BOUND = 2;
+
+  /**
+   * Recursive implementation of binary search.
+   *
+   * @param aLow Indices here and lower do not contain the needle.
+   * @param aHigh Indices here and higher do not contain the needle.
+   * @param aNeedle The element being searched for.
+   * @param aHaystack The non-empty array being searched.
+   * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+   * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
+   *     'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
+   *     closest element that is smaller than or greater than the one we are
+   *     searching for, respectively, if the exact element cannot be found.
+   */
+  function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) {
+    // This function terminates when one of the following is true:
+    //
+    //   1. We find the exact element we are looking for.
+    //
+    //   2. We did not find the exact element, but we can return the index of
+    //      the next-closest element.
+    //
+    //   3. We did not find the exact element, and there is no next-closest
+    //      element than the one we are searching for, so we return -1.
+    var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+    var cmp = aCompare(aNeedle, aHaystack[mid], true);
+    if (cmp === 0) {
+      // Found the element we are looking for.
+      return mid;
+    }
+    else if (cmp > 0) {
+      // Our needle is greater than aHaystack[mid].
+      if (aHigh - mid > 1) {
+        // The element is in the upper half.
+        return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias);
+      }
+
+      // The exact needle element was not found in this haystack. Determine if
+      // we are in termination case (3) or (2) and return the appropriate thing.
+      if (aBias == exports.LEAST_UPPER_BOUND) {
+        return aHigh < aHaystack.length ? aHigh : -1;
+      } else {
+        return mid;
+      }
+    }
+    else {
+      // Our needle is less than aHaystack[mid].
+      if (mid - aLow > 1) {
+        // The element is in the lower half.
+        return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias);
+      }
+
+      // we are in termination case (3) or (2) and return the appropriate thing.
+      if (aBias == exports.LEAST_UPPER_BOUND) {
+        return mid;
+      } else {
+        return aLow < 0 ? -1 : aLow;
+      }
+    }
+  }
+
+  /**
+   * This is an implementation of binary search which will always try and return
+   * the index of the closest element if there is no exact hit. This is because
+   * mappings between original and generated line/col pairs are single points,
+   * and there is an implicit region between each of them, so a miss just means
+   * that you aren't on the very start of a region.
+   *
+   * @param aNeedle The element you are looking for.
+   * @param aHaystack The array that is being searched.
+   * @param aCompare A function which takes the needle and an element in the
+   *     array and returns -1, 0, or 1 depending on whether the needle is less
+   *     than, equal to, or greater than the element, respectively.
+   * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
+   *     'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
+   *     closest element that is smaller than or greater than the one we are
+   *     searching for, respectively, if the exact element cannot be found.
+   *     Defaults to 'binarySearch.GREATEST_LOWER_BOUND'.
+   */
+  exports.search = function search(aNeedle, aHaystack, aCompare, aBias) {
+    if (aHaystack.length === 0) {
+      return -1;
+    }
+
+    var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack,
+                                aCompare, aBias || exports.GREATEST_LOWER_BOUND);
+    if (index < 0) {
+      return -1;
+    }
+
+    // We have found either the exact element, or the next-closest element than
+    // the one we are searching for. However, there may be more than one such
+    // element. Make sure we always return the smallest of these.
+    while (index - 1 >= 0) {
+      if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) {
+        break;
+      }
+      --index;
+    }
+
+    return index;
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/mapping-list.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/mapping-list.js
new file mode 100644
index 0000000..01aff22
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/mapping-list.js
@@ -0,0 +1,86 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+
+  /**
+   * Determine whether mappingB is after mappingA with respect to generated
+   * position.
+   */
+  function generatedPositionAfter(mappingA, mappingB) {
+    // Optimized for most common case
+    var lineA = mappingA.generatedLine;
+    var lineB = mappingB.generatedLine;
+    var columnA = mappingA.generatedColumn;
+    var columnB = mappingB.generatedColumn;
+    return lineB > lineA || lineB == lineA && columnB >= columnA ||
+           util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0;
+  }
+
+  /**
+   * A data structure to provide a sorted view of accumulated mappings in a
+   * performance conscious manner. It trades a neglibable overhead in general
+   * case for a large speedup in case of mappings being added in order.
+   */
+  function MappingList() {
+    this._array = [];
+    this._sorted = true;
+    // Serves as infimum
+    this._last = {generatedLine: -1, generatedColumn: 0};
+  }
+
+  /**
+   * Iterate through internal items. This method takes the same arguments that
+   * `Array.prototype.forEach` takes.
+   *
+   * NOTE: The order of the mappings is NOT guaranteed.
+   */
+  MappingList.prototype.unsortedForEach =
+    function MappingList_forEach(aCallback, aThisArg) {
+      this._array.forEach(aCallback, aThisArg);
+    };
+
+  /**
+   * Add the given source mapping.
+   *
+   * @param Object aMapping
+   */
+  MappingList.prototype.add = function MappingList_add(aMapping) {
+    var mapping;
+    if (generatedPositionAfter(this._last, aMapping)) {
+      this._last = aMapping;
+      this._array.push(aMapping);
+    } else {
+      this._sorted = false;
+      this._array.push(aMapping);
+    }
+  };
+
+  /**
+   * Returns the flat, sorted array of mappings. The mappings are sorted by
+   * generated position.
+   *
+   * WARNING: This method returns internal data without copying, for
+   * performance. The return value must NOT be mutated, and should be treated as
+   * an immutable borrow. If you want to take ownership, you must make your own
+   * copy.
+   */
+  MappingList.prototype.toArray = function MappingList_toArray() {
+    if (!this._sorted) {
+      this._array.sort(util.compareByGeneratedPositionsInflated);
+      this._sorted = true;
+    }
+    return this._array;
+  };
+
+  exports.MappingList = MappingList;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/quick-sort.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/quick-sort.js
new file mode 100644
index 0000000..e0551ed
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/quick-sort.js
@@ -0,0 +1,120 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  // It turns out that some (most?) JavaScript engines don't self-host
+  // `Array.prototype.sort`. This makes sense because C++ will likely remain
+  // faster than JS when doing raw CPU-intensive sorting. However, when using a
+  // custom comparator function, calling back and forth between the VM's C++ and
+  // JIT'd JS is rather slow *and* loses JIT type information, resulting in
+  // worse generated code for the comparator function than would be optimal. In
+  // fact, when sorting with a comparator, these costs outweigh the benefits of
+  // sorting in C++. By using our own JS-implemented Quick Sort (below), we get
+  // a ~3500ms mean speed-up in `bench/bench.html`.
+
+  /**
+   * Swap the elements indexed by `x` and `y` in the array `ary`.
+   *
+   * @param {Array} ary
+   *        The array.
+   * @param {Number} x
+   *        The index of the first item.
+   * @param {Number} y
+   *        The index of the second item.
+   */
+  function swap(ary, x, y) {
+    var temp = ary[x];
+    ary[x] = ary[y];
+    ary[y] = temp;
+  }
+
+  /**
+   * Returns a random integer within the range `low .. high` inclusive.
+   *
+   * @param {Number} low
+   *        The lower bound on the range.
+   * @param {Number} high
+   *        The upper bound on the range.
+   */
+  function randomIntInRange(low, high) {
+    return Math.round(low + (Math.random() * (high - low)));
+  }
+
+  /**
+   * The Quick Sort algorithm.
+   *
+   * @param {Array} ary
+   *        An array to sort.
+   * @param {function} comparator
+   *        Function to use to compare two items.
+   * @param {Number} p
+   *        Start index of the array
+   * @param {Number} r
+   *        End index of the array
+   */
+  function doQuickSort(ary, comparator, p, r) {
+    // If our lower bound is less than our upper bound, we (1) partition the
+    // array into two pieces and (2) recurse on each half. If it is not, this is
+    // the empty array and our base case.
+
+    if (p < r) {
+      // (1) Partitioning.
+      //
+      // The partitioning chooses a pivot between `p` and `r` and moves all
+      // elements that are less than or equal to the pivot to the before it, and
+      // all the elements that are greater than it after it. The effect is that
+      // once partition is done, the pivot is in the exact place it will be when
+      // the array is put in sorted order, and it will not need to be moved
+      // again. This runs in O(n) time.
+
+      // Always choose a random pivot so that an input array which is reverse
+      // sorted does not cause O(n^2) running time.
+      var pivotIndex = randomIntInRange(p, r);
+      var i = p - 1;
+
+      swap(ary, pivotIndex, r);
+      var pivot = ary[r];
+
+      // Immediately after `j` is incremented in this loop, the following hold
+      // true:
+      //
+      //   * Every element in `ary[p .. i]` is less than or equal to the pivot.
+      //
+      //   * Every element in `ary[i+1 .. j-1]` is greater than the pivot.
+      for (var j = p; j < r; j++) {
+        if (comparator(ary[j], pivot) <= 0) {
+          i += 1;
+          swap(ary, i, j);
+        }
+      }
+
+      swap(ary, i + 1, j);
+      var q = i + 1;
+
+      // (2) Recurse on each half.
+
+      doQuickSort(ary, comparator, p, q - 1);
+      doQuickSort(ary, comparator, q + 1, r);
+    }
+  }
+
+  /**
+   * Sort the given array in-place with the given comparator function.
+   *
+   * @param {Array} ary
+   *        An array to sort.
+   * @param {function} comparator
+   *        Function to use to compare two items.
+   */
+  exports.quickSort = function (ary, comparator) {
+    doQuickSort(ary, comparator, 0, ary.length - 1);
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-map-consumer.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-map-consumer.js
new file mode 100644
index 0000000..cbdc467
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-map-consumer.js
@@ -0,0 +1,1077 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+  var binarySearch = require('./binary-search');
+  var ArraySet = require('./array-set').ArraySet;
+  var base64VLQ = require('./base64-vlq');
+  var quickSort = require('./quick-sort').quickSort;
+
+  function SourceMapConsumer(aSourceMap) {
+    var sourceMap = aSourceMap;
+    if (typeof aSourceMap === 'string') {
+      sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    }
+
+    return sourceMap.sections != null
+      ? new IndexedSourceMapConsumer(sourceMap)
+      : new BasicSourceMapConsumer(sourceMap);
+  }
+
+  SourceMapConsumer.fromSourceMap = function(aSourceMap) {
+    return BasicSourceMapConsumer.fromSourceMap(aSourceMap);
+  }
+
+  /**
+   * The version of the source mapping spec that we are consuming.
+   */
+  SourceMapConsumer.prototype._version = 3;
+
+  // `__generatedMappings` and `__originalMappings` are arrays that hold the
+  // parsed mapping coordinates from the source map's "mappings" attribute. They
+  // are lazily instantiated, accessed via the `_generatedMappings` and
+  // `_originalMappings` getters respectively, and we only parse the mappings
+  // and create these arrays once queried for a source location. We jump through
+  // these hoops because there can be many thousands of mappings, and parsing
+  // them is expensive, so we only want to do it if we must.
+  //
+  // Each object in the arrays is of the form:
+  //
+  //     {
+  //       generatedLine: The line number in the generated code,
+  //       generatedColumn: The column number in the generated code,
+  //       source: The path to the original source file that generated this
+  //               chunk of code,
+  //       originalLine: The line number in the original source that
+  //                     corresponds to this chunk of generated code,
+  //       originalColumn: The column number in the original source that
+  //                       corresponds to this chunk of generated code,
+  //       name: The name of the original symbol which generated this chunk of
+  //             code.
+  //     }
+  //
+  // All properties except for `generatedLine` and `generatedColumn` can be
+  // `null`.
+  //
+  // `_generatedMappings` is ordered by the generated positions.
+  //
+  // `_originalMappings` is ordered by the original positions.
+
+  SourceMapConsumer.prototype.__generatedMappings = null;
+  Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+    get: function () {
+      if (!this.__generatedMappings) {
+        this._parseMappings(this._mappings, this.sourceRoot);
+      }
+
+      return this.__generatedMappings;
+    }
+  });
+
+  SourceMapConsumer.prototype.__originalMappings = null;
+  Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+    get: function () {
+      if (!this.__originalMappings) {
+        this._parseMappings(this._mappings, this.sourceRoot);
+      }
+
+      return this.__originalMappings;
+    }
+  });
+
+  SourceMapConsumer.prototype._charIsMappingSeparator =
+    function SourceMapConsumer_charIsMappingSeparator(aStr, index) {
+      var c = aStr.charAt(index);
+      return c === ";" || c === ",";
+    };
+
+  /**
+   * Parse the mappings in a string in to a data structure which we can easily
+   * query (the ordered arrays in the `this.__generatedMappings` and
+   * `this.__originalMappings` properties).
+   */
+  SourceMapConsumer.prototype._parseMappings =
+    function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+      throw new Error("Subclasses must implement _parseMappings");
+    };
+
+  SourceMapConsumer.GENERATED_ORDER = 1;
+  SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+  SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
+  SourceMapConsumer.LEAST_UPPER_BOUND = 2;
+
+  /**
+   * Iterate over each mapping between an original source/line/column and a
+   * generated line/column in this source map.
+   *
+   * @param Function aCallback
+   *        The function that is called with each mapping.
+   * @param Object aContext
+   *        Optional. If specified, this object will be the value of `this` every
+   *        time that `aCallback` is called.
+   * @param aOrder
+   *        Either `SourceMapConsumer.GENERATED_ORDER` or
+   *        `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+   *        iterate over the mappings sorted by the generated file's line/column
+   *        order or the original's source/line/column order, respectively. Defaults to
+   *        `SourceMapConsumer.GENERATED_ORDER`.
+   */
+  SourceMapConsumer.prototype.eachMapping =
+    function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+      var context = aContext || null;
+      var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+      var mappings;
+      switch (order) {
+      case SourceMapConsumer.GENERATED_ORDER:
+        mappings = this._generatedMappings;
+        break;
+      case SourceMapConsumer.ORIGINAL_ORDER:
+        mappings = this._originalMappings;
+        break;
+      default:
+        throw new Error("Unknown order of iteration.");
+      }
+
+      var sourceRoot = this.sourceRoot;
+      mappings.map(function (mapping) {
+        var source = mapping.source === null ? null : this._sources.at(mapping.source);
+        if (source != null && sourceRoot != null) {
+          source = util.join(sourceRoot, source);
+        }
+        return {
+          source: source,
+          generatedLine: mapping.generatedLine,
+          generatedColumn: mapping.generatedColumn,
+          originalLine: mapping.originalLine,
+          originalColumn: mapping.originalColumn,
+          name: mapping.name === null ? null : this._names.at(mapping.name)
+        };
+      }, this).forEach(aCallback, context);
+    };
+
+  /**
+   * Returns all generated line and column information for the original source,
+   * line, and column provided. If no column is provided, returns all mappings
+   * corresponding to a either the line we are searching for or the next
+   * closest line that has any mappings. Otherwise, returns all mappings
+   * corresponding to the given line and either the column we are searching for
+   * or the next closest column that has any offsets.
+   *
+   * The only argument is an object with the following properties:
+   *
+   *   - source: The filename of the original source.
+   *   - line: The line number in the original source.
+   *   - column: Optional. the column number in the original source.
+   *
+   * and an array of objects is returned, each with the following properties:
+   *
+   *   - line: The line number in the generated source, or null.
+   *   - column: The column number in the generated source, or null.
+   */
+  SourceMapConsumer.prototype.allGeneratedPositionsFor =
+    function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
+      var line = util.getArg(aArgs, 'line');
+
+      // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
+      // returns the index of the closest mapping less than the needle. By
+      // setting needle.originalColumn to 0, we thus find the last mapping for
+      // the given line, provided such a mapping exists.
+      var needle = {
+        source: util.getArg(aArgs, 'source'),
+        originalLine: line,
+        originalColumn: util.getArg(aArgs, 'column', 0)
+      };
+
+      if (this.sourceRoot != null) {
+        needle.source = util.relative(this.sourceRoot, needle.source);
+      }
+      if (!this._sources.has(needle.source)) {
+        return [];
+      }
+      needle.source = this._sources.indexOf(needle.source);
+
+      var mappings = [];
+
+      var index = this._findMapping(needle,
+                                    this._originalMappings,
+                                    "originalLine",
+                                    "originalColumn",
+                                    util.compareByOriginalPositions,
+                                    binarySearch.LEAST_UPPER_BOUND);
+      if (index >= 0) {
+        var mapping = this._originalMappings[index];
+
+        if (aArgs.column === undefined) {
+          var originalLine = mapping.originalLine;
+
+          // Iterate until either we run out of mappings, or we run into
+          // a mapping for a different line than the one we found. Since
+          // mappings are sorted, this is guaranteed to find all mappings for
+          // the line we found.
+          while (mapping && mapping.originalLine === originalLine) {
+            mappings.push({
+              line: util.getArg(mapping, 'generatedLine', null),
+              column: util.getArg(mapping, 'generatedColumn', null),
+              lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+            });
+
+            mapping = this._originalMappings[++index];
+          }
+        } else {
+          var originalColumn = mapping.originalColumn;
+
+          // Iterate until either we run out of mappings, or we run into
+          // a mapping for a different line than the one we were searching for.
+          // Since mappings are sorted, this is guaranteed to find all mappings for
+          // the line we are searching for.
+          while (mapping &&
+                 mapping.originalLine === line &&
+                 mapping.originalColumn == originalColumn) {
+            mappings.push({
+              line: util.getArg(mapping, 'generatedLine', null),
+              column: util.getArg(mapping, 'generatedColumn', null),
+              lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+            });
+
+            mapping = this._originalMappings[++index];
+          }
+        }
+      }
+
+      return mappings;
+    };
+
+  exports.SourceMapConsumer = SourceMapConsumer;
+
+  /**
+   * A BasicSourceMapConsumer instance represents a parsed source map which we can
+   * query for information about the original file positions by giving it a file
+   * position in the generated source.
+   *
+   * The only parameter is the raw source map (either as a JSON string, or
+   * already parsed to an object). According to the spec, source maps have the
+   * following attributes:
+   *
+   *   - version: Which version of the source map spec this map is following.
+   *   - sources: An array of URLs to the original source files.
+   *   - names: An array of identifiers which can be referrenced by individual mappings.
+   *   - sourceRoot: Optional. The URL root from which all sources are relative.
+   *   - sourcesContent: Optional. An array of contents of the original source files.
+   *   - mappings: A string of base64 VLQs which contain the actual mappings.
+   *   - file: Optional. The generated file this source map is associated with.
+   *
+   * Here is an example source map, taken from the source map spec[0]:
+   *
+   *     {
+   *       version : 3,
+   *       file: "out.js",
+   *       sourceRoot : "",
+   *       sources: ["foo.js", "bar.js"],
+   *       names: ["src", "maps", "are", "fun"],
+   *       mappings: "AA,AB;;ABCDE;"
+   *     }
+   *
+   * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+   */
+  function BasicSourceMapConsumer(aSourceMap) {
+    var sourceMap = aSourceMap;
+    if (typeof aSourceMap === 'string') {
+      sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    }
+
+    var version = util.getArg(sourceMap, 'version');
+    var sources = util.getArg(sourceMap, 'sources');
+    // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
+    // requires the array) to play nice here.
+    var names = util.getArg(sourceMap, 'names', []);
+    var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+    var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
+    var mappings = util.getArg(sourceMap, 'mappings');
+    var file = util.getArg(sourceMap, 'file', null);
+
+    // Once again, Sass deviates from the spec and supplies the version as a
+    // string rather than a number, so we use loose equality checking here.
+    if (version != this._version) {
+      throw new Error('Unsupported version: ' + version);
+    }
+
+    // Some source maps produce relative source paths like "./foo.js" instead of
+    // "foo.js".  Normalize these first so that future comparisons will succeed.
+    // See bugzil.la/1090768.
+    sources = sources.map(util.normalize);
+
+    // Pass `true` below to allow duplicate names and sources. While source maps
+    // are intended to be compressed and deduplicated, the TypeScript compiler
+    // sometimes generates source maps with duplicates in them. See Github issue
+    // #72 and bugzil.la/889492.
+    this._names = ArraySet.fromArray(names, true);
+    this._sources = ArraySet.fromArray(sources, true);
+
+    this.sourceRoot = sourceRoot;
+    this.sourcesContent = sourcesContent;
+    this._mappings = mappings;
+    this.file = file;
+  }
+
+  BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
+  BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
+
+  /**
+   * Create a BasicSourceMapConsumer from a SourceMapGenerator.
+   *
+   * @param SourceMapGenerator aSourceMap
+   *        The source map that will be consumed.
+   * @returns BasicSourceMapConsumer
+   */
+  BasicSourceMapConsumer.fromSourceMap =
+    function SourceMapConsumer_fromSourceMap(aSourceMap) {
+      var smc = Object.create(BasicSourceMapConsumer.prototype);
+
+      var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
+      var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
+      smc.sourceRoot = aSourceMap._sourceRoot;
+      smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
+                                                              smc.sourceRoot);
+      smc.file = aSourceMap._file;
+
+      // Because we are modifying the entries (by converting string sources and
+      // names to indices into the sources and names ArraySets), we have to make
+      // a copy of the entry or else bad things happen. Shared mutable state
+      // strikes again! See github issue #191.
+
+      var generatedMappings = aSourceMap._mappings.toArray().slice();
+      var destGeneratedMappings = smc.__generatedMappings = [];
+      var destOriginalMappings = smc.__originalMappings = [];
+
+      for (var i = 0, length = generatedMappings.length; i < length; i++) {
+        var srcMapping = generatedMappings[i];
+        var destMapping = new Mapping;
+        destMapping.generatedLine = srcMapping.generatedLine;
+        destMapping.generatedColumn = srcMapping.generatedColumn;
+
+        if (srcMapping.source) {
+          destMapping.source = sources.indexOf(srcMapping.source);
+          destMapping.originalLine = srcMapping.originalLine;
+          destMapping.originalColumn = srcMapping.originalColumn;
+
+          if (srcMapping.name) {
+            destMapping.name = names.indexOf(srcMapping.name);
+          }
+
+          destOriginalMappings.push(destMapping);
+        }
+
+        destGeneratedMappings.push(destMapping);
+      }
+
+      quickSort(smc.__originalMappings, util.compareByOriginalPositions);
+
+      return smc;
+    };
+
+  /**
+   * The version of the source mapping spec that we are consuming.
+   */
+  BasicSourceMapConsumer.prototype._version = 3;
+
+  /**
+   * The list of original sources.
+   */
+  Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
+    get: function () {
+      return this._sources.toArray().map(function (s) {
+        return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
+      }, this);
+    }
+  });
+
+  /**
+   * Provide the JIT with a nice shape / hidden class.
+   */
+  function Mapping() {
+    this.generatedLine = 0;
+    this.generatedColumn = 0;
+    this.source = null;
+    this.originalLine = null;
+    this.originalColumn = null;
+    this.name = null;
+  }
+
+  /**
+   * Parse the mappings in a string in to a data structure which we can easily
+   * query (the ordered arrays in the `this.__generatedMappings` and
+   * `this.__originalMappings` properties).
+   */
+  BasicSourceMapConsumer.prototype._parseMappings =
+    function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+      var generatedLine = 1;
+      var previousGeneratedColumn = 0;
+      var previousOriginalLine = 0;
+      var previousOriginalColumn = 0;
+      var previousSource = 0;
+      var previousName = 0;
+      var length = aStr.length;
+      var index = 0;
+      var cachedSegments = {};
+      var temp = {};
+      var originalMappings = [];
+      var generatedMappings = [];
+      var mapping, str, segment, end, value;
+
+      while (index < length) {
+        if (aStr.charAt(index) === ';') {
+          generatedLine++;
+          index++;
+          previousGeneratedColumn = 0;
+        }
+        else if (aStr.charAt(index) === ',') {
+          index++;
+        }
+        else {
+          mapping = new Mapping();
+          mapping.generatedLine = generatedLine;
+
+          // Because each offset is encoded relative to the previous one,
+          // many segments often have the same encoding. We can exploit this
+          // fact by caching the parsed variable length fields of each segment,
+          // allowing us to avoid a second parse if we encounter the same
+          // segment again.
+          for (end = index; end < length; end++) {
+            if (this._charIsMappingSeparator(aStr, end)) {
+              break;
+            }
+          }
+          str = aStr.slice(index, end);
+
+          segment = cachedSegments[str];
+          if (segment) {
+            index += str.length;
+          } else {
+            segment = [];
+            while (index < end) {
+              base64VLQ.decode(aStr, index, temp);
+              value = temp.value;
+              index = temp.rest;
+              segment.push(value);
+            }
+
+            if (segment.length === 2) {
+              throw new Error('Found a source, but no line and column');
+            }
+
+            if (segment.length === 3) {
+              throw new Error('Found a source and line, but no column');
+            }
+
+            cachedSegments[str] = segment;
+          }
+
+          // Generated column.
+          mapping.generatedColumn = previousGeneratedColumn + segment[0];
+          previousGeneratedColumn = mapping.generatedColumn;
+
+          if (segment.length > 1) {
+            // Original source.
+            mapping.source = previousSource + segment[1];
+            previousSource += segment[1];
+
+            // Original line.
+            mapping.originalLine = previousOriginalLine + segment[2];
+            previousOriginalLine = mapping.originalLine;
+            // Lines are stored 0-based
+            mapping.originalLine += 1;
+
+            // Original column.
+            mapping.originalColumn = previousOriginalColumn + segment[3];
+            previousOriginalColumn = mapping.originalColumn;
+
+            if (segment.length > 4) {
+              // Original name.
+              mapping.name = previousName + segment[4];
+              previousName += segment[4];
+            }
+          }
+
+          generatedMappings.push(mapping);
+          if (typeof mapping.originalLine === 'number') {
+            originalMappings.push(mapping);
+          }
+        }
+      }
+
+      quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
+      this.__generatedMappings = generatedMappings;
+
+      quickSort(originalMappings, util.compareByOriginalPositions);
+      this.__originalMappings = originalMappings;
+    };
+
+  /**
+   * Find the mapping that best matches the hypothetical "needle" mapping that
+   * we are searching for in the given "haystack" of mappings.
+   */
+  BasicSourceMapConsumer.prototype._findMapping =
+    function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
+                                           aColumnName, aComparator, aBias) {
+      // To return the position we are searching for, we must first find the
+      // mapping for the given position and then return the opposite position it
+      // points to. Because the mappings are sorted, we can use binary search to
+      // find the best mapping.
+
+      if (aNeedle[aLineName] <= 0) {
+        throw new TypeError('Line must be greater than or equal to 1, got '
+                            + aNeedle[aLineName]);
+      }
+      if (aNeedle[aColumnName] < 0) {
+        throw new TypeError('Column must be greater than or equal to 0, got '
+                            + aNeedle[aColumnName]);
+      }
+
+      return binarySearch.search(aNeedle, aMappings, aComparator, aBias);
+    };
+
+  /**
+   * Compute the last column for each generated mapping. The last column is
+   * inclusive.
+   */
+  BasicSourceMapConsumer.prototype.computeColumnSpans =
+    function SourceMapConsumer_computeColumnSpans() {
+      for (var index = 0; index < this._generatedMappings.length; ++index) {
+        var mapping = this._generatedMappings[index];
+
+        // Mappings do not contain a field for the last generated columnt. We
+        // can come up with an optimistic estimate, however, by assuming that
+        // mappings are contiguous (i.e. given two consecutive mappings, the
+        // first mapping ends where the second one starts).
+        if (index + 1 < this._generatedMappings.length) {
+          var nextMapping = this._generatedMappings[index + 1];
+
+          if (mapping.generatedLine === nextMapping.generatedLine) {
+            mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
+            continue;
+          }
+        }
+
+        // The last mapping for each line spans the entire line.
+        mapping.lastGeneratedColumn = Infinity;
+      }
+    };
+
+  /**
+   * Returns the original source, line, and column information for the generated
+   * source's line and column positions provided. The only argument is an object
+   * with the following properties:
+   *
+   *   - line: The line number in the generated source.
+   *   - column: The column number in the generated source.
+   *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
+   *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
+   *     closest element that is smaller than or greater than the one we are
+   *     searching for, respectively, if the exact element cannot be found.
+   *     Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - source: The original source file, or null.
+   *   - line: The line number in the original source, or null.
+   *   - column: The column number in the original source, or null.
+   *   - name: The original identifier, or null.
+   */
+  BasicSourceMapConsumer.prototype.originalPositionFor =
+    function SourceMapConsumer_originalPositionFor(aArgs) {
+      var needle = {
+        generatedLine: util.getArg(aArgs, 'line'),
+        generatedColumn: util.getArg(aArgs, 'column')
+      };
+
+      var index = this._findMapping(
+        needle,
+        this._generatedMappings,
+        "generatedLine",
+        "generatedColumn",
+        util.compareByGeneratedPositionsDeflated,
+        util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
+      );
+
+      if (index >= 0) {
+        var mapping = this._generatedMappings[index];
+
+        if (mapping.generatedLine === needle.generatedLine) {
+          var source = util.getArg(mapping, 'source', null);
+          if (source !== null) {
+            source = this._sources.at(source);
+            if (this.sourceRoot != null) {
+              source = util.join(this.sourceRoot, source);
+            }
+          }
+          var name = util.getArg(mapping, 'name', null);
+          if (name !== null) {
+            name = this._names.at(name);
+          }
+          return {
+            source: source,
+            line: util.getArg(mapping, 'originalLine', null),
+            column: util.getArg(mapping, 'originalColumn', null),
+            name: name
+          };
+        }
+      }
+
+      return {
+        source: null,
+        line: null,
+        column: null,
+        name: null
+      };
+    };
+
+  /**
+   * Return true if we have the source content for every source in the source
+   * map, false otherwise.
+   */
+  BasicSourceMapConsumer.prototype.hasContentsOfAllSources =
+    function BasicSourceMapConsumer_hasContentsOfAllSources() {
+      if (!this.sourcesContent) {
+        return false;
+      }
+      return this.sourcesContent.length >= this._sources.size() &&
+        !this.sourcesContent.some(function (sc) { return sc == null; });
+    };
+
+  /**
+   * Returns the original source content. The only argument is the url of the
+   * original source file. Returns null if no original source content is
+   * availible.
+   */
+  BasicSourceMapConsumer.prototype.sourceContentFor =
+    function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
+      if (!this.sourcesContent) {
+        return null;
+      }
+
+      if (this.sourceRoot != null) {
+        aSource = util.relative(this.sourceRoot, aSource);
+      }
+
+      if (this._sources.has(aSource)) {
+        return this.sourcesContent[this._sources.indexOf(aSource)];
+      }
+
+      var url;
+      if (this.sourceRoot != null
+          && (url = util.urlParse(this.sourceRoot))) {
+        // XXX: file:// URIs and absolute paths lead to unexpected behavior for
+        // many users. We can help them out when they expect file:// URIs to
+        // behave like it would if they were running a local HTTP server. See
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
+        var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+        if (url.scheme == "file"
+            && this._sources.has(fileUriAbsPath)) {
+          return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
+        }
+
+        if ((!url.path || url.path == "/")
+            && this._sources.has("/" + aSource)) {
+          return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+        }
+      }
+
+      // This function is used recursively from
+      // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we
+      // don't want to throw if we can't find the source - we just want to
+      // return null, so we provide a flag to exit gracefully.
+      if (nullOnMissing) {
+        return null;
+      }
+      else {
+        throw new Error('"' + aSource + '" is not in the SourceMap.');
+      }
+    };
+
+  /**
+   * Returns the generated line and column information for the original source,
+   * line, and column positions provided. The only argument is an object with
+   * the following properties:
+   *
+   *   - source: The filename of the original source.
+   *   - line: The line number in the original source.
+   *   - column: The column number in the original source.
+   *   - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
+   *     'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
+   *     closest element that is smaller than or greater than the one we are
+   *     searching for, respectively, if the exact element cannot be found.
+   *     Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - line: The line number in the generated source, or null.
+   *   - column: The column number in the generated source, or null.
+   */
+  BasicSourceMapConsumer.prototype.generatedPositionFor =
+    function SourceMapConsumer_generatedPositionFor(aArgs) {
+      var source = util.getArg(aArgs, 'source');
+      if (this.sourceRoot != null) {
+        source = util.relative(this.sourceRoot, source);
+      }
+      if (!this._sources.has(source)) {
+        return {
+          line: null,
+          column: null,
+          lastColumn: null
+        };
+      }
+      source = this._sources.indexOf(source);
+
+      var needle = {
+        source: source,
+        originalLine: util.getArg(aArgs, 'line'),
+        originalColumn: util.getArg(aArgs, 'column')
+      };
+
+      var index = this._findMapping(
+        needle,
+        this._originalMappings,
+        "originalLine",
+        "originalColumn",
+        util.compareByOriginalPositions,
+        util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
+      );
+
+      if (index >= 0) {
+        var mapping = this._originalMappings[index];
+
+        if (mapping.source === needle.source) {
+          return {
+            line: util.getArg(mapping, 'generatedLine', null),
+            column: util.getArg(mapping, 'generatedColumn', null),
+            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+          };
+        }
+      }
+
+      return {
+        line: null,
+        column: null,
+        lastColumn: null
+      };
+    };
+
+  exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
+
+  /**
+   * An IndexedSourceMapConsumer instance represents a parsed source map which
+   * we can query for information. It differs from BasicSourceMapConsumer in
+   * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
+   * input.
+   *
+   * The only parameter is a raw source map (either as a JSON string, or already
+   * parsed to an object). According to the spec for indexed source maps, they
+   * have the following attributes:
+   *
+   *   - version: Which version of the source map spec this map is following.
+   *   - file: Optional. The generated file this source map is associated with.
+   *   - sections: A list of section definitions.
+   *
+   * Each value under the "sections" field has two fields:
+   *   - offset: The offset into the original specified at which this section
+   *       begins to apply, defined as an object with a "line" and "column"
+   *       field.
+   *   - map: A source map definition. This source map could also be indexed,
+   *       but doesn't have to be.
+   *
+   * Instead of the "map" field, it's also possible to have a "url" field
+   * specifying a URL to retrieve a source map from, but that's currently
+   * unsupported.
+   *
+   * Here's an example source map, taken from the source map spec[0], but
+   * modified to omit a section which uses the "url" field.
+   *
+   *  {
+   *    version : 3,
+   *    file: "app.js",
+   *    sections: [{
+   *      offset: {line:100, column:10},
+   *      map: {
+   *        version : 3,
+   *        file: "section.js",
+   *        sources: ["foo.js", "bar.js"],
+   *        names: ["src", "maps", "are", "fun"],
+   *        mappings: "AAAA,E;;ABCDE;"
+   *      }
+   *    }],
+   *  }
+   *
+   * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
+   */
+  function IndexedSourceMapConsumer(aSourceMap) {
+    var sourceMap = aSourceMap;
+    if (typeof aSourceMap === 'string') {
+      sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    }
+
+    var version = util.getArg(sourceMap, 'version');
+    var sections = util.getArg(sourceMap, 'sections');
+
+    if (version != this._version) {
+      throw new Error('Unsupported version: ' + version);
+    }
+
+    this._sources = new ArraySet();
+    this._names = new ArraySet();
+
+    var lastOffset = {
+      line: -1,
+      column: 0
+    };
+    this._sections = sections.map(function (s) {
+      if (s.url) {
+        // The url field will require support for asynchronicity.
+        // See https://github.com/mozilla/source-map/issues/16
+        throw new Error('Support for url field in sections not implemented.');
+      }
+      var offset = util.getArg(s, 'offset');
+      var offsetLine = util.getArg(offset, 'line');
+      var offsetColumn = util.getArg(offset, 'column');
+
+      if (offsetLine < lastOffset.line ||
+          (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) {
+        throw new Error('Section offsets must be ordered and non-overlapping.');
+      }
+      lastOffset = offset;
+
+      return {
+        generatedOffset: {
+          // The offset fields are 0-based, but we use 1-based indices when
+          // encoding/decoding from VLQ.
+          generatedLine: offsetLine + 1,
+          generatedColumn: offsetColumn + 1
+        },
+        consumer: new SourceMapConsumer(util.getArg(s, 'map'))
+      }
+    });
+  }
+
+  IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
+  IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;
+
+  /**
+   * The version of the source mapping spec that we are consuming.
+   */
+  IndexedSourceMapConsumer.prototype._version = 3;
+
+  /**
+   * The list of original sources.
+   */
+  Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
+    get: function () {
+      var sources = [];
+      for (var i = 0; i < this._sections.length; i++) {
+        for (var j = 0; j < this._sections[i].consumer.sources.length; j++) {
+          sources.push(this._sections[i].consumer.sources[j]);
+        }
+      };
+      return sources;
+    }
+  });
+
+  /**
+   * Returns the original source, line, and column information for the generated
+   * source's line and column positions provided. The only argument is an object
+   * with the following properties:
+   *
+   *   - line: The line number in the generated source.
+   *   - column: The column number in the generated source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - source: The original source file, or null.
+   *   - line: The line number in the original source, or null.
+   *   - column: The column number in the original source, or null.
+   *   - name: The original identifier, or null.
+   */
+  IndexedSourceMapConsumer.prototype.originalPositionFor =
+    function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
+      var needle = {
+        generatedLine: util.getArg(aArgs, 'line'),
+        generatedColumn: util.getArg(aArgs, 'column')
+      };
+
+      // Find the section containing the generated position we're trying to map
+      // to an original position.
+      var sectionIndex = binarySearch.search(needle, this._sections,
+        function(needle, section) {
+          var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
+          if (cmp) {
+            return cmp;
+          }
+
+          return (needle.generatedColumn -
+                  section.generatedOffset.generatedColumn);
+        });
+      var section = this._sections[sectionIndex];
+
+      if (!section) {
+        return {
+          source: null,
+          line: null,
+          column: null,
+          name: null
+        };
+      }
+
+      return section.consumer.originalPositionFor({
+        line: needle.generatedLine -
+          (section.generatedOffset.generatedLine - 1),
+        column: needle.generatedColumn -
+          (section.generatedOffset.generatedLine === needle.generatedLine
+           ? section.generatedOffset.generatedColumn - 1
+           : 0),
+        bias: aArgs.bias
+      });
+    };
+
+  /**
+   * Return true if we have the source content for every source in the source
+   * map, false otherwise.
+   */
+  IndexedSourceMapConsumer.prototype.hasContentsOfAllSources =
+    function IndexedSourceMapConsumer_hasContentsOfAllSources() {
+      return this._sections.every(function (s) {
+        return s.consumer.hasContentsOfAllSources();
+      });
+    };
+
+  /**
+   * Returns the original source content. The only argument is the url of the
+   * original source file. Returns null if no original source content is
+   * available.
+   */
+  IndexedSourceMapConsumer.prototype.sourceContentFor =
+    function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
+      for (var i = 0; i < this._sections.length; i++) {
+        var section = this._sections[i];
+
+        var content = section.consumer.sourceContentFor(aSource, true);
+        if (content) {
+          return content;
+        }
+      }
+      if (nullOnMissing) {
+        return null;
+      }
+      else {
+        throw new Error('"' + aSource + '" is not in the SourceMap.');
+      }
+    };
+
+  /**
+   * Returns the generated line and column information for the original source,
+   * line, and column positions provided. The only argument is an object with
+   * the following properties:
+   *
+   *   - source: The filename of the original source.
+   *   - line: The line number in the original source.
+   *   - column: The column number in the original source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - line: The line number in the generated source, or null.
+   *   - column: The column number in the generated source, or null.
+   */
+  IndexedSourceMapConsumer.prototype.generatedPositionFor =
+    function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
+      for (var i = 0; i < this._sections.length; i++) {
+        var section = this._sections[i];
+
+        // Only consider this section if the requested source is in the list of
+        // sources of the consumer.
+        if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) {
+          continue;
+        }
+        var generatedPosition = section.consumer.generatedPositionFor(aArgs);
+        if (generatedPosition) {
+          var ret = {
+            line: generatedPosition.line +
+              (section.generatedOffset.generatedLine - 1),
+            column: generatedPosition.column +
+              (section.generatedOffset.generatedLine === generatedPosition.line
+               ? section.generatedOffset.generatedColumn - 1
+               : 0)
+          };
+          return ret;
+        }
+      }
+
+      return {
+        line: null,
+        column: null
+      };
+    };
+
+  /**
+   * Parse the mappings in a string in to a data structure which we can easily
+   * query (the ordered arrays in the `this.__generatedMappings` and
+   * `this.__originalMappings` properties).
+   */
+  IndexedSourceMapConsumer.prototype._parseMappings =
+    function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+      this.__generatedMappings = [];
+      this.__originalMappings = [];
+      for (var i = 0; i < this._sections.length; i++) {
+        var section = this._sections[i];
+        var sectionMappings = section.consumer._generatedMappings;
+        for (var j = 0; j < sectionMappings.length; j++) {
+          var mapping = sectionMappings[i];
+
+          var source = section.consumer._sources.at(mapping.source);
+          if (section.consumer.sourceRoot !== null) {
+            source = util.join(section.consumer.sourceRoot, source);
+          }
+          this._sources.add(source);
+          source = this._sources.indexOf(source);
+
+          var name = section.consumer._names.at(mapping.name);
+          this._names.add(name);
+          name = this._names.indexOf(name);
+
+          // The mappings coming from the consumer for the section have
+          // generated positions relative to the start of the section, so we
+          // need to offset them to be relative to the start of the concatenated
+          // generated file.
+          var adjustedMapping = {
+            source: source,
+            generatedLine: mapping.generatedLine +
+              (section.generatedOffset.generatedLine - 1),
+            generatedColumn: mapping.column +
+              (section.generatedOffset.generatedLine === mapping.generatedLine)
+              ? section.generatedOffset.generatedColumn - 1
+              : 0,
+            originalLine: mapping.originalLine,
+            originalColumn: mapping.originalColumn,
+            name: name
+          };
+
+          this.__generatedMappings.push(adjustedMapping);
+          if (typeof adjustedMapping.originalLine === 'number') {
+            this.__originalMappings.push(adjustedMapping);
+          }
+        };
+      };
+
+      quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
+      quickSort(this.__originalMappings, util.compareByOriginalPositions);
+    };
+
+  exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-map-generator.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-map-generator.js
new file mode 100644
index 0000000..d8a9025
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-map-generator.js
@@ -0,0 +1,399 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64VLQ = require('./base64-vlq');
+  var util = require('./util');
+  var ArraySet = require('./array-set').ArraySet;
+  var MappingList = require('./mapping-list').MappingList;
+
+  /**
+   * An instance of the SourceMapGenerator represents a source map which is
+   * being built incrementally. You may pass an object with the following
+   * properties:
+   *
+   *   - file: The filename of the generated source.
+   *   - sourceRoot: A root for all relative URLs in this source map.
+   */
+  function SourceMapGenerator(aArgs) {
+    if (!aArgs) {
+      aArgs = {};
+    }
+    this._file = util.getArg(aArgs, 'file', null);
+    this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+    this._skipValidation = util.getArg(aArgs, 'skipValidation', false);
+    this._sources = new ArraySet();
+    this._names = new ArraySet();
+    this._mappings = new MappingList();
+    this._sourcesContents = null;
+  }
+
+  SourceMapGenerator.prototype._version = 3;
+
+  /**
+   * Creates a new SourceMapGenerator based on a SourceMapConsumer
+   *
+   * @param aSourceMapConsumer The SourceMap.
+   */
+  SourceMapGenerator.fromSourceMap =
+    function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
+      var sourceRoot = aSourceMapConsumer.sourceRoot;
+      var generator = new SourceMapGenerator({
+        file: aSourceMapConsumer.file,
+        sourceRoot: sourceRoot
+      });
+      aSourceMapConsumer.eachMapping(function (mapping) {
+        var newMapping = {
+          generated: {
+            line: mapping.generatedLine,
+            column: mapping.generatedColumn
+          }
+        };
+
+        if (mapping.source != null) {
+          newMapping.source = mapping.source;
+          if (sourceRoot != null) {
+            newMapping.source = util.relative(sourceRoot, newMapping.source);
+          }
+
+          newMapping.original = {
+            line: mapping.originalLine,
+            column: mapping.originalColumn
+          };
+
+          if (mapping.name != null) {
+            newMapping.name = mapping.name;
+          }
+        }
+
+        generator.addMapping(newMapping);
+      });
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content != null) {
+          generator.setSourceContent(sourceFile, content);
+        }
+      });
+      return generator;
+    };
+
+  /**
+   * Add a single mapping from original source line and column to the generated
+   * source's line and column for this source map being created. The mapping
+   * object should have the following properties:
+   *
+   *   - generated: An object with the generated line and column positions.
+   *   - original: An object with the original line and column positions.
+   *   - source: The original source file (relative to the sourceRoot).
+   *   - name: An optional original token name for this mapping.
+   */
+  SourceMapGenerator.prototype.addMapping =
+    function SourceMapGenerator_addMapping(aArgs) {
+      var generated = util.getArg(aArgs, 'generated');
+      var original = util.getArg(aArgs, 'original', null);
+      var source = util.getArg(aArgs, 'source', null);
+      var name = util.getArg(aArgs, 'name', null);
+
+      if (!this._skipValidation) {
+        this._validateMapping(generated, original, source, name);
+      }
+
+      if (source != null && !this._sources.has(source)) {
+        this._sources.add(source);
+      }
+
+      if (name != null && !this._names.has(name)) {
+        this._names.add(name);
+      }
+
+      this._mappings.add({
+        generatedLine: generated.line,
+        generatedColumn: generated.column,
+        originalLine: original != null && original.line,
+        originalColumn: original != null && original.column,
+        source: source,
+        name: name
+      });
+    };
+
+  /**
+   * Set the source content for a source file.
+   */
+  SourceMapGenerator.prototype.setSourceContent =
+    function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
+      var source = aSourceFile;
+      if (this._sourceRoot != null) {
+        source = util.relative(this._sourceRoot, source);
+      }
+
+      if (aSourceContent != null) {
+        // Add the source content to the _sourcesContents map.
+        // Create a new _sourcesContents map if the property is null.
+        if (!this._sourcesContents) {
+          this._sourcesContents = {};
+        }
+        this._sourcesContents[util.toSetString(source)] = aSourceContent;
+      } else if (this._sourcesContents) {
+        // Remove the source file from the _sourcesContents map.
+        // If the _sourcesContents map is empty, set the property to null.
+        delete this._sourcesContents[util.toSetString(source)];
+        if (Object.keys(this._sourcesContents).length === 0) {
+          this._sourcesContents = null;
+        }
+      }
+    };
+
+  /**
+   * Applies the mappings of a sub-source-map for a specific source file to the
+   * source map being generated. Each mapping to the supplied source file is
+   * rewritten using the supplied source map. Note: The resolution for the
+   * resulting mappings is the minimium of this map and the supplied map.
+   *
+   * @param aSourceMapConsumer The source map to be applied.
+   * @param aSourceFile Optional. The filename of the source file.
+   *        If omitted, SourceMapConsumer's file property will be used.
+   * @param aSourceMapPath Optional. The dirname of the path to the source map
+   *        to be applied. If relative, it is relative to the SourceMapConsumer.
+   *        This parameter is needed when the two source maps aren't in the same
+   *        directory, and the source map to be applied contains relative source
+   *        paths. If so, those relative source paths need to be rewritten
+   *        relative to the SourceMapGenerator.
+   */
+  SourceMapGenerator.prototype.applySourceMap =
+    function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
+      var sourceFile = aSourceFile;
+      // If aSourceFile is omitted, we will use the file property of the SourceMap
+      if (aSourceFile == null) {
+        if (aSourceMapConsumer.file == null) {
+          throw new Error(
+            'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
+            'or the source map\'s "file" property. Both were omitted.'
+          );
+        }
+        sourceFile = aSourceMapConsumer.file;
+      }
+      var sourceRoot = this._sourceRoot;
+      // Make "sourceFile" relative if an absolute Url is passed.
+      if (sourceRoot != null) {
+        sourceFile = util.relative(sourceRoot, sourceFile);
+      }
+      // Applying the SourceMap can add and remove items from the sources and
+      // the names array.
+      var newSources = new ArraySet();
+      var newNames = new ArraySet();
+
+      // Find mappings for the "sourceFile"
+      this._mappings.unsortedForEach(function (mapping) {
+        if (mapping.source === sourceFile && mapping.originalLine != null) {
+          // Check if it can be mapped by the source map, then update the mapping.
+          var original = aSourceMapConsumer.originalPositionFor({
+            line: mapping.originalLine,
+            column: mapping.originalColumn
+          });
+          if (original.source != null) {
+            // Copy mapping
+            mapping.source = original.source;
+            if (aSourceMapPath != null) {
+              mapping.source = util.join(aSourceMapPath, mapping.source)
+            }
+            if (sourceRoot != null) {
+              mapping.source = util.relative(sourceRoot, mapping.source);
+            }
+            mapping.originalLine = original.line;
+            mapping.originalColumn = original.column;
+            if (original.name != null) {
+              mapping.name = original.name;
+            }
+          }
+        }
+
+        var source = mapping.source;
+        if (source != null && !newSources.has(source)) {
+          newSources.add(source);
+        }
+
+        var name = mapping.name;
+        if (name != null && !newNames.has(name)) {
+          newNames.add(name);
+        }
+
+      }, this);
+      this._sources = newSources;
+      this._names = newNames;
+
+      // Copy sourcesContents of applied map.
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content != null) {
+          if (aSourceMapPath != null) {
+            sourceFile = util.join(aSourceMapPath, sourceFile);
+          }
+          if (sourceRoot != null) {
+            sourceFile = util.relative(sourceRoot, sourceFile);
+          }
+          this.setSourceContent(sourceFile, content);
+        }
+      }, this);
+    };
+
+  /**
+   * A mapping can have one of the three levels of data:
+   *
+   *   1. Just the generated position.
+   *   2. The Generated position, original position, and original source.
+   *   3. Generated and original position, original source, as well as a name
+   *      token.
+   *
+   * To maintain consistency, we validate that any new mapping being added falls
+   * in to one of these categories.
+   */
+  SourceMapGenerator.prototype._validateMapping =
+    function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+                                                aName) {
+      if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+          && aGenerated.line > 0 && aGenerated.column >= 0
+          && !aOriginal && !aSource && !aName) {
+        // Case 1.
+        return;
+      }
+      else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+               && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+               && aGenerated.line > 0 && aGenerated.column >= 0
+               && aOriginal.line > 0 && aOriginal.column >= 0
+               && aSource) {
+        // Cases 2 and 3.
+        return;
+      }
+      else {
+        throw new Error('Invalid mapping: ' + JSON.stringify({
+          generated: aGenerated,
+          source: aSource,
+          original: aOriginal,
+          name: aName
+        }));
+      }
+    };
+
+  /**
+   * Serialize the accumulated mappings in to the stream of base 64 VLQs
+   * specified by the source map format.
+   */
+  SourceMapGenerator.prototype._serializeMappings =
+    function SourceMapGenerator_serializeMappings() {
+      var previousGeneratedColumn = 0;
+      var previousGeneratedLine = 1;
+      var previousOriginalColumn = 0;
+      var previousOriginalLine = 0;
+      var previousName = 0;
+      var previousSource = 0;
+      var result = '';
+      var mapping;
+
+      var mappings = this._mappings.toArray();
+      for (var i = 0, len = mappings.length; i < len; i++) {
+        mapping = mappings[i];
+
+        if (mapping.generatedLine !== previousGeneratedLine) {
+          previousGeneratedColumn = 0;
+          while (mapping.generatedLine !== previousGeneratedLine) {
+            result += ';';
+            previousGeneratedLine++;
+          }
+        }
+        else {
+          if (i > 0) {
+            if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) {
+              continue;
+            }
+            result += ',';
+          }
+        }
+
+        result += base64VLQ.encode(mapping.generatedColumn
+                                   - previousGeneratedColumn);
+        previousGeneratedColumn = mapping.generatedColumn;
+
+        if (mapping.source != null) {
+          result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+                                     - previousSource);
+          previousSource = this._sources.indexOf(mapping.source);
+
+          // lines are stored 0-based in SourceMap spec version 3
+          result += base64VLQ.encode(mapping.originalLine - 1
+                                     - previousOriginalLine);
+          previousOriginalLine = mapping.originalLine - 1;
+
+          result += base64VLQ.encode(mapping.originalColumn
+                                     - previousOriginalColumn);
+          previousOriginalColumn = mapping.originalColumn;
+
+          if (mapping.name != null) {
+            result += base64VLQ.encode(this._names.indexOf(mapping.name)
+                                       - previousName);
+            previousName = this._names.indexOf(mapping.name);
+          }
+        }
+      }
+
+      return result;
+    };
+
+  SourceMapGenerator.prototype._generateSourcesContent =
+    function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
+      return aSources.map(function (source) {
+        if (!this._sourcesContents) {
+          return null;
+        }
+        if (aSourceRoot != null) {
+          source = util.relative(aSourceRoot, source);
+        }
+        var key = util.toSetString(source);
+        return Object.prototype.hasOwnProperty.call(this._sourcesContents,
+                                                    key)
+          ? this._sourcesContents[key]
+          : null;
+      }, this);
+    };
+
+  /**
+   * Externalize the source map.
+   */
+  SourceMapGenerator.prototype.toJSON =
+    function SourceMapGenerator_toJSON() {
+      var map = {
+        version: this._version,
+        sources: this._sources.toArray(),
+        names: this._names.toArray(),
+        mappings: this._serializeMappings()
+      };
+      if (this._file != null) {
+        map.file = this._file;
+      }
+      if (this._sourceRoot != null) {
+        map.sourceRoot = this._sourceRoot;
+      }
+      if (this._sourcesContents) {
+        map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
+      }
+
+      return map;
+    };
+
+  /**
+   * Render the source map being generated to a string.
+   */
+  SourceMapGenerator.prototype.toString =
+    function SourceMapGenerator_toString() {
+      return JSON.stringify(this.toJSON());
+    };
+
+  exports.SourceMapGenerator = SourceMapGenerator;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-node.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-node.js
new file mode 100644
index 0000000..9ee90bd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/source-node.js
@@ -0,0 +1,414 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
+  var util = require('./util');
+
+  // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
+  // operating systems these days (capturing the result).
+  var REGEX_NEWLINE = /(\r?\n)/;
+
+  // Newline character code for charCodeAt() comparisons
+  var NEWLINE_CODE = 10;
+
+  // Private symbol for identifying `SourceNode`s when multiple versions of
+  // the source-map library are loaded. This MUST NOT CHANGE across
+  // versions!
+  var isSourceNode = "$$$isSourceNode$$$";
+
+  /**
+   * SourceNodes provide a way to abstract over interpolating/concatenating
+   * snippets of generated JavaScript source code while maintaining the line and
+   * column information associated with the original source code.
+   *
+   * @param aLine The original line number.
+   * @param aColumn The original column number.
+   * @param aSource The original source's filename.
+   * @param aChunks Optional. An array of strings which are snippets of
+   *        generated JS, or other SourceNodes.
+   * @param aName The original identifier.
+   */
+  function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
+    this.children = [];
+    this.sourceContents = {};
+    this.line = aLine == null ? null : aLine;
+    this.column = aColumn == null ? null : aColumn;
+    this.source = aSource == null ? null : aSource;
+    this.name = aName == null ? null : aName;
+    this[isSourceNode] = true;
+    if (aChunks != null) this.add(aChunks);
+  }
+
+  /**
+   * Creates a SourceNode from generated code and a SourceMapConsumer.
+   *
+   * @param aGeneratedCode The generated code
+   * @param aSourceMapConsumer The SourceMap for the generated code
+   * @param aRelativePath Optional. The path that relative sources in the
+   *        SourceMapConsumer should be relative to.
+   */
+  SourceNode.fromStringWithSourceMap =
+    function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
+      // The SourceNode we want to fill with the generated code
+      // and the SourceMap
+      var node = new SourceNode();
+
+      // All even indices of this array are one line of the generated code,
+      // while all odd indices are the newlines between two adjacent lines
+      // (since `REGEX_NEWLINE` captures its match).
+      // Processed fragments are removed from this array, by calling `shiftNextLine`.
+      var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+      var shiftNextLine = function() {
+        var lineContents = remainingLines.shift();
+        // The last line of a file might not have a newline.
+        var newLine = remainingLines.shift() || "";
+        return lineContents + newLine;
+      };
+
+      // We need to remember the position of "remainingLines"
+      var lastGeneratedLine = 1, lastGeneratedColumn = 0;
+
+      // The generate SourceNodes we need a code range.
+      // To extract it current and last mapping is used.
+      // Here we store the last mapping.
+      var lastMapping = null;
+
+      aSourceMapConsumer.eachMapping(function (mapping) {
+        if (lastMapping !== null) {
+          // We add the code from "lastMapping" to "mapping":
+          // First check if there is a new line in between.
+          if (lastGeneratedLine < mapping.generatedLine) {
+            var code = "";
+            // Associate first line with "lastMapping"
+            addMappingWithCode(lastMapping, shiftNextLine());
+            lastGeneratedLine++;
+            lastGeneratedColumn = 0;
+            // The remaining code is added without mapping
+          } else {
+            // There is no new line in between.
+            // Associate the code between "lastGeneratedColumn" and
+            // "mapping.generatedColumn" with "lastMapping"
+            var nextLine = remainingLines[0];
+            var code = nextLine.substr(0, mapping.generatedColumn -
+                                          lastGeneratedColumn);
+            remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+                                                lastGeneratedColumn);
+            lastGeneratedColumn = mapping.generatedColumn;
+            addMappingWithCode(lastMapping, code);
+            // No more remaining code, continue
+            lastMapping = mapping;
+            return;
+          }
+        }
+        // We add the generated code until the first mapping
+        // to the SourceNode without any mapping.
+        // Each line is added as separate string.
+        while (lastGeneratedLine < mapping.generatedLine) {
+          node.add(shiftNextLine());
+          lastGeneratedLine++;
+        }
+        if (lastGeneratedColumn < mapping.generatedColumn) {
+          var nextLine = remainingLines[0];
+          node.add(nextLine.substr(0, mapping.generatedColumn));
+          remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+          lastGeneratedColumn = mapping.generatedColumn;
+        }
+        lastMapping = mapping;
+      }, this);
+      // We have processed all mappings.
+      if (remainingLines.length > 0) {
+        if (lastMapping) {
+          // Associate the remaining code in the current line with "lastMapping"
+          addMappingWithCode(lastMapping, shiftNextLine());
+        }
+        // and add the remaining lines without any mapping
+        node.add(remainingLines.join(""));
+      }
+
+      // Copy sourcesContent into SourceNode
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content != null) {
+          if (aRelativePath != null) {
+            sourceFile = util.join(aRelativePath, sourceFile);
+          }
+          node.setSourceContent(sourceFile, content);
+        }
+      });
+
+      return node;
+
+      function addMappingWithCode(mapping, code) {
+        if (mapping === null || mapping.source === undefined) {
+          node.add(code);
+        } else {
+          var source = aRelativePath
+            ? util.join(aRelativePath, mapping.source)
+            : mapping.source;
+          node.add(new SourceNode(mapping.originalLine,
+                                  mapping.originalColumn,
+                                  source,
+                                  code,
+                                  mapping.name));
+        }
+      }
+    };
+
+  /**
+   * Add a chunk of generated JS to this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.add = function SourceNode_add(aChunk) {
+    if (Array.isArray(aChunk)) {
+      aChunk.forEach(function (chunk) {
+        this.add(chunk);
+      }, this);
+    }
+    else if (aChunk[isSourceNode] || typeof aChunk === "string") {
+      if (aChunk) {
+        this.children.push(aChunk);
+      }
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Add a chunk of generated JS to the beginning of this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+    if (Array.isArray(aChunk)) {
+      for (var i = aChunk.length-1; i >= 0; i--) {
+        this.prepend(aChunk[i]);
+      }
+    }
+    else if (aChunk[isSourceNode] || typeof aChunk === "string") {
+      this.children.unshift(aChunk);
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Walk over the tree of JS snippets in this node and its children. The
+   * walking function is called once for each snippet of JS and is passed that
+   * snippet and the its original associated source's line/column location.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+    var chunk;
+    for (var i = 0, len = this.children.length; i < len; i++) {
+      chunk = this.children[i];
+      if (chunk[isSourceNode]) {
+        chunk.walk(aFn);
+      }
+      else {
+        if (chunk !== '') {
+          aFn(chunk, { source: this.source,
+                       line: this.line,
+                       column: this.column,
+                       name: this.name });
+        }
+      }
+    }
+  };
+
+  /**
+   * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+   * each of `this.children`.
+   *
+   * @param aSep The separator.
+   */
+  SourceNode.prototype.join = function SourceNode_join(aSep) {
+    var newChildren;
+    var i;
+    var len = this.children.length;
+    if (len > 0) {
+      newChildren = [];
+      for (i = 0; i < len-1; i++) {
+        newChildren.push(this.children[i]);
+        newChildren.push(aSep);
+      }
+      newChildren.push(this.children[i]);
+      this.children = newChildren;
+    }
+    return this;
+  };
+
+  /**
+   * Call String.prototype.replace on the very right-most source snippet. Useful
+   * for trimming whitespace from the end of a source node, etc.
+   *
+   * @param aPattern The pattern to replace.
+   * @param aReplacement The thing to replace the pattern with.
+   */
+  SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+    var lastChild = this.children[this.children.length - 1];
+    if (lastChild[isSourceNode]) {
+      lastChild.replaceRight(aPattern, aReplacement);
+    }
+    else if (typeof lastChild === 'string') {
+      this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
+    }
+    else {
+      this.children.push(''.replace(aPattern, aReplacement));
+    }
+    return this;
+  };
+
+  /**
+   * Set the source content for a source file. This will be added to the SourceMapGenerator
+   * in the sourcesContent field.
+   *
+   * @param aSourceFile The filename of the source file
+   * @param aSourceContent The content of the source file
+   */
+  SourceNode.prototype.setSourceContent =
+    function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
+      this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+    };
+
+  /**
+   * Walk over the tree of SourceNodes. The walking function is called for each
+   * source file content and is passed the filename and source content.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walkSourceContents =
+    function SourceNode_walkSourceContents(aFn) {
+      for (var i = 0, len = this.children.length; i < len; i++) {
+        if (this.children[i][isSourceNode]) {
+          this.children[i].walkSourceContents(aFn);
+        }
+      }
+
+      var sources = Object.keys(this.sourceContents);
+      for (var i = 0, len = sources.length; i < len; i++) {
+        aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
+      }
+    };
+
+  /**
+   * Return the string representation of this source node. Walks over the tree
+   * and concatenates all the various snippets together to one string.
+   */
+  SourceNode.prototype.toString = function SourceNode_toString() {
+    var str = "";
+    this.walk(function (chunk) {
+      str += chunk;
+    });
+    return str;
+  };
+
+  /**
+   * Returns the string representation of this source node along with a source
+   * map.
+   */
+  SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+    var generated = {
+      code: "",
+      line: 1,
+      column: 0
+    };
+    var map = new SourceMapGenerator(aArgs);
+    var sourceMappingActive = false;
+    var lastOriginalSource = null;
+    var lastOriginalLine = null;
+    var lastOriginalColumn = null;
+    var lastOriginalName = null;
+    this.walk(function (chunk, original) {
+      generated.code += chunk;
+      if (original.source !== null
+          && original.line !== null
+          && original.column !== null) {
+        if(lastOriginalSource !== original.source
+           || lastOriginalLine !== original.line
+           || lastOriginalColumn !== original.column
+           || lastOriginalName !== original.name) {
+          map.addMapping({
+            source: original.source,
+            original: {
+              line: original.line,
+              column: original.column
+            },
+            generated: {
+              line: generated.line,
+              column: generated.column
+            },
+            name: original.name
+          });
+        }
+        lastOriginalSource = original.source;
+        lastOriginalLine = original.line;
+        lastOriginalColumn = original.column;
+        lastOriginalName = original.name;
+        sourceMappingActive = true;
+      } else if (sourceMappingActive) {
+        map.addMapping({
+          generated: {
+            line: generated.line,
+            column: generated.column
+          }
+        });
+        lastOriginalSource = null;
+        sourceMappingActive = false;
+      }
+      for (var idx = 0, length = chunk.length; idx < length; idx++) {
+        if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
+          generated.line++;
+          generated.column = 0;
+          // Mappings end at eol
+          if (idx + 1 === length) {
+            lastOriginalSource = null;
+            sourceMappingActive = false;
+          } else if (sourceMappingActive) {
+            map.addMapping({
+              source: original.source,
+              original: {
+                line: original.line,
+                column: original.column
+              },
+              generated: {
+                line: generated.line,
+                column: generated.column
+              },
+              name: original.name
+            });
+          }
+        } else {
+          generated.column++;
+        }
+      }
+    });
+    this.walkSourceContents(function (sourceFile, sourceContent) {
+      map.setSourceContent(sourceFile, sourceContent);
+    });
+
+    return { code: generated.code, map: map };
+  };
+
+  exports.SourceNode = SourceNode;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/util.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/util.js
new file mode 100644
index 0000000..0b9d75d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/lib/source-map/util.js
@@ -0,0 +1,370 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  /**
+   * This is a helper function for getting values from parameter/options
+   * objects.
+   *
+   * @param args The object we are extracting values from
+   * @param name The name of the property we are getting.
+   * @param defaultValue An optional value to return if the property is missing
+   * from the object. If this is not specified and the property is missing, an
+   * error will be thrown.
+   */
+  function getArg(aArgs, aName, aDefaultValue) {
+    if (aName in aArgs) {
+      return aArgs[aName];
+    } else if (arguments.length === 3) {
+      return aDefaultValue;
+    } else {
+      throw new Error('"' + aName + '" is a required argument.');
+    }
+  }
+  exports.getArg = getArg;
+
+  var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+  var dataUrlRegexp = /^data:.+\,.+$/;
+
+  function urlParse(aUrl) {
+    var match = aUrl.match(urlRegexp);
+    if (!match) {
+      return null;
+    }
+    return {
+      scheme: match[1],
+      auth: match[2],
+      host: match[3],
+      port: match[4],
+      path: match[5]
+    };
+  }
+  exports.urlParse = urlParse;
+
+  function urlGenerate(aParsedUrl) {
+    var url = '';
+    if (aParsedUrl.scheme) {
+      url += aParsedUrl.scheme + ':';
+    }
+    url += '//';
+    if (aParsedUrl.auth) {
+      url += aParsedUrl.auth + '@';
+    }
+    if (aParsedUrl.host) {
+      url += aParsedUrl.host;
+    }
+    if (aParsedUrl.port) {
+      url += ":" + aParsedUrl.port
+    }
+    if (aParsedUrl.path) {
+      url += aParsedUrl.path;
+    }
+    return url;
+  }
+  exports.urlGenerate = urlGenerate;
+
+  /**
+   * Normalizes a path, or the path portion of a URL:
+   *
+   * - Replaces consequtive slashes with one slash.
+   * - Removes unnecessary '.' parts.
+   * - Removes unnecessary '<dir>/..' parts.
+   *
+   * Based on code in the Node.js 'path' core module.
+   *
+   * @param aPath The path or url to normalize.
+   */
+  function normalize(aPath) {
+    var path = aPath;
+    var url = urlParse(aPath);
+    if (url) {
+      if (!url.path) {
+        return aPath;
+      }
+      path = url.path;
+    }
+    var isAbsolute = (path.charAt(0) === '/');
+
+    var parts = path.split(/\/+/);
+    for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
+      part = parts[i];
+      if (part === '.') {
+        parts.splice(i, 1);
+      } else if (part === '..') {
+        up++;
+      } else if (up > 0) {
+        if (part === '') {
+          // The first part is blank if the path is absolute. Trying to go
+          // above the root is a no-op. Therefore we can remove all '..' parts
+          // directly after the root.
+          parts.splice(i + 1, up);
+          up = 0;
+        } else {
+          parts.splice(i, 2);
+          up--;
+        }
+      }
+    }
+    path = parts.join('/');
+
+    if (path === '') {
+      path = isAbsolute ? '/' : '.';
+    }
+
+    if (url) {
+      url.path = path;
+      return urlGenerate(url);
+    }
+    return path;
+  }
+  exports.normalize = normalize;
+
+  /**
+   * Joins two paths/URLs.
+   *
+   * @param aRoot The root path or URL.
+   * @param aPath The path or URL to be joined with the root.
+   *
+   * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
+   *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
+   *   first.
+   * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
+   *   is updated with the result and aRoot is returned. Otherwise the result
+   *   is returned.
+   *   - If aPath is absolute, the result is aPath.
+   *   - Otherwise the two paths are joined with a slash.
+   * - Joining for example 'http://' and 'www.example.com' is also supported.
+   */
+  function join(aRoot, aPath) {
+    if (aRoot === "") {
+      aRoot = ".";
+    }
+    if (aPath === "") {
+      aPath = ".";
+    }
+    var aPathUrl = urlParse(aPath);
+    var aRootUrl = urlParse(aRoot);
+    if (aRootUrl) {
+      aRoot = aRootUrl.path || '/';
+    }
+
+    // `join(foo, '//www.example.org')`
+    if (aPathUrl && !aPathUrl.scheme) {
+      if (aRootUrl) {
+        aPathUrl.scheme = aRootUrl.scheme;
+      }
+      return urlGenerate(aPathUrl);
+    }
+
+    if (aPathUrl || aPath.match(dataUrlRegexp)) {
+      return aPath;
+    }
+
+    // `join('http://', 'www.example.com')`
+    if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
+      aRootUrl.host = aPath;
+      return urlGenerate(aRootUrl);
+    }
+
+    var joined = aPath.charAt(0) === '/'
+      ? aPath
+      : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
+
+    if (aRootUrl) {
+      aRootUrl.path = joined;
+      return urlGenerate(aRootUrl);
+    }
+    return joined;
+  }
+  exports.join = join;
+
+  /**
+   * Make a path relative to a URL or another path.
+   *
+   * @param aRoot The root path or URL.
+   * @param aPath The path or URL to be made relative to aRoot.
+   */
+  function relative(aRoot, aPath) {
+    if (aRoot === "") {
+      aRoot = ".";
+    }
+
+    aRoot = aRoot.replace(/\/$/, '');
+
+    // It is possible for the path to be above the root. In this case, simply
+    // checking whether the root is a prefix of the path won't work. Instead, we
+    // need to remove components from the root one by one, until either we find
+    // a prefix that fits, or we run out of components to remove.
+    var level = 0;
+    while (aPath.indexOf(aRoot + '/') !== 0) {
+      var index = aRoot.lastIndexOf("/");
+      if (index < 0) {
+        return aPath;
+      }
+
+      // If the only part of the root that is left is the scheme (i.e. http://,
+      // file:///, etc.), one or more slashes (/), or simply nothing at all, we
+      // have exhausted all components, so the path is not relative to the root.
+      aRoot = aRoot.slice(0, index);
+      if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
+        return aPath;
+      }
+
+      ++level;
+    }
+
+    // Make sure we add a "../" for each component we removed from the root.
+    return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
+  }
+  exports.relative = relative;
+
+  /**
+   * Because behavior goes wacky when you set `__proto__` on objects, we
+   * have to prefix all the strings in our set with an arbitrary character.
+   *
+   * See https://github.com/mozilla/source-map/pull/31 and
+   * https://github.com/mozilla/source-map/issues/30
+   *
+   * @param String aStr
+   */
+  function toSetString(aStr) {
+    return '$' + aStr;
+  }
+  exports.toSetString = toSetString;
+
+  function fromSetString(aStr) {
+    return aStr.substr(1);
+  }
+  exports.fromSetString = fromSetString;
+
+  /**
+   * Comparator between two mappings where the original positions are compared.
+   *
+   * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+   * mappings with the same original source/line/column, but different generated
+   * line and column the same. Useful when searching for a mapping with a
+   * stubbed out mapping.
+   */
+  function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
+    var cmp = mappingA.source - mappingB.source;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp !== 0 || onlyCompareOriginal) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    return mappingA.name - mappingB.name;
+  };
+  exports.compareByOriginalPositions = compareByOriginalPositions;
+
+  /**
+   * Comparator between two mappings with deflated source and name indices where
+   * the generated positions are compared.
+   *
+   * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+   * mappings with the same generated line and column, but different
+   * source/name/original line and column the same. Useful when searching for a
+   * mapping with a stubbed out mapping.
+   */
+  function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
+    var cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+    if (cmp !== 0 || onlyCompareGenerated) {
+      return cmp;
+    }
+
+    cmp = mappingA.source - mappingB.source;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    return mappingA.name - mappingB.name;
+  };
+  exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
+
+  function strcmp(aStr1, aStr2) {
+    if (aStr1 === aStr2) {
+      return 0;
+    }
+
+    if (aStr1 > aStr2) {
+      return 1;
+    }
+
+    return -1;
+  }
+
+  /**
+   * Comparator between two mappings with inflated source and name strings where
+   * the generated positions are compared.
+   */
+  function compareByGeneratedPositionsInflated(mappingA, mappingB) {
+    var cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = strcmp(mappingA.source, mappingB.source);
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp !== 0) {
+      return cmp;
+    }
+
+    return strcmp(mappingA.name, mappingB.name);
+  };
+  exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/LICENSE
new file mode 100644
index 0000000..af46c6d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/LICENSE
@@ -0,0 +1,58 @@
+amdefine is released under two licenses: new BSD, and MIT. You may pick the
+license that best suits your development needs. The text of both licenses are
+provided below.
+
+
+The "New" BSD License:
+----------------------
+
+Copyright (c) 2011-2015, The Dojo Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  * Neither the name of the Dojo Foundation nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+MIT License
+-----------
+
+Copyright (c) 2011-2015, The Dojo Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/README.md
new file mode 100644
index 0000000..037a6e8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/README.md
@@ -0,0 +1,171 @@
+# amdefine
+
+A module that can be used to implement AMD's define() in Node. This allows you
+to code to the AMD API and have the module work in node programs without
+requiring those other programs to use AMD.
+
+## Usage
+
+**1)** Update your package.json to indicate amdefine as a dependency:
+
+```javascript
+    "dependencies": {
+        "amdefine": ">=0.1.0"
+    }
+```
+
+Then run `npm install` to get amdefine into your project.
+
+**2)** At the top of each module that uses define(), place this code:
+
+```javascript
+if (typeof define !== 'function') { var define = require('amdefine')(module) }
+```
+
+**Only use these snippets** when loading amdefine. If you preserve the basic structure,
+with the braces, it will be stripped out when using the [RequireJS optimizer](#optimizer).
+
+You can add spaces, line breaks and even require amdefine with a local path, but
+keep the rest of the structure to get the stripping behavior.
+
+As you may know, because `if` statements in JavaScript don't have their own scope, the var
+declaration in the above snippet is made whether the `if` expression is truthy or not. If
+RequireJS is loaded then the declaration is superfluous because `define` is already already
+declared in the same scope in RequireJS. Fortunately JavaScript handles multiple `var`
+declarations of the same variable in the same scope gracefully.
+
+If you want to deliver amdefine.js with your code rather than specifying it as a dependency
+with npm, then just download the latest release and refer to it using a relative path:
+
+[Latest Version](https://github.com/jrburke/amdefine/raw/latest/amdefine.js)
+
+### amdefine/intercept
+
+Consider this very experimental.
+
+Instead of pasting the piece of text for the amdefine setup of a `define`
+variable in each module you create or consume, you can use `amdefine/intercept`
+instead. It will automatically insert the above snippet in each .js file loaded
+by Node.
+
+**Warning**: you should only use this if you are creating an application that
+is consuming AMD style defined()'d modules that are distributed via npm and want
+to run that code in Node.
+
+For library code where you are not sure if it will be used by others in Node or
+in the browser, then explicitly depending on amdefine and placing the code
+snippet above is suggested path, instead of using `amdefine/intercept`. The
+intercept module affects all .js files loaded in the Node app, and it is
+inconsiderate to modify global state like that unless you are also controlling
+the top level app.
+
+#### Why distribute AMD-style modules via npm?
+
+npm has a lot of weaknesses for front-end use (installed layout is not great,
+should have better support for the `baseUrl + moduleID + '.js' style of loading,
+single file JS installs), but some people want a JS package manager and are
+willing to live with those constraints. If that is you, but still want to author
+in AMD style modules to get dynamic require([]), better direct source usage and
+powerful loader plugin support in the browser, then this tool can help.
+
+#### amdefine/intercept usage
+
+Just require it in your top level app module (for example index.js, server.js):
+
+```javascript
+require('amdefine/intercept');
+```
+
+The module does not return a value, so no need to assign the result to a local
+variable.
+
+Then just require() code as you normally would with Node's require(). Any .js
+loaded after the intercept require will have the amdefine check injected in
+the .js source as it is loaded. It does not modify the source on disk, just
+prepends some content to the text of the module as it is loaded by Node.
+
+#### How amdefine/intercept works
+
+It overrides the `Module._extensions['.js']` in Node to automatically prepend
+the amdefine snippet above. So, it will affect any .js file loaded by your
+app.
+
+## define() usage
+
+It is best if you use the anonymous forms of define() in your module:
+
+```javascript
+define(function (require) {
+    var dependency = require('dependency');
+});
+```
+
+or
+
+```javascript
+define(['dependency'], function (dependency) {
+
+});
+```
+
+## RequireJS optimizer integration. <a name="optimizer"></name>
+
+Version 1.0.3 of the [RequireJS optimizer](http://requirejs.org/docs/optimization.html)
+will have support for stripping the `if (typeof define !== 'function')` check
+mentioned above, so you can include this snippet for code that runs in the
+browser, but avoid taking the cost of the if() statement once the code is
+optimized for deployment.
+
+## Node 0.4 Support
+
+If you want to support Node 0.4, then add `require` as the second parameter to amdefine:
+
+```javascript
+//Only if you want Node 0.4. If using 0.5 or later, use the above snippet.
+if (typeof define !== 'function') { var define = require('amdefine')(module, require) }
+```
+
+## Limitations
+
+### Synchronous vs Asynchronous
+
+amdefine creates a define() function that is callable by your code. It will
+execute and trace dependencies and call the factory function *synchronously*,
+to keep the behavior in line with Node's synchronous dependency tracing.
+
+The exception: calling AMD's callback-style require() from inside a factory
+function. The require callback is called on process.nextTick():
+
+```javascript
+define(function (require) {
+    require(['a'], function(a) {
+        //'a' is loaded synchronously, but
+        //this callback is called on process.nextTick().
+    });
+});
+```
+
+### Loader Plugins
+
+Loader plugins are supported as long as they call their load() callbacks
+synchronously. So ones that do network requests will not work. However plugins
+like [text](http://requirejs.org/docs/api.html#text) can load text files locally.
+
+The plugin API's `load.fromText()` is **not supported** in amdefine, so this means
+transpiler plugins like the [CoffeeScript loader plugin](https://github.com/jrburke/require-cs)
+will not work. This may be fixable, but it is a bit complex, and I do not have
+enough node-fu to figure it out yet. See the source for amdefine.js if you want
+to get an idea of the issues involved.
+
+## Tests
+
+To run the tests, cd to **tests** and run:
+
+```
+node all.js
+node all-intercept.js
+```
+
+## License
+
+New BSD and MIT. Check the LICENSE file for all the details.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/amdefine.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/amdefine.js
new file mode 100644
index 0000000..0c4a954
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/amdefine.js
@@ -0,0 +1,301 @@
+/** vim: et:ts=4:sw=4:sts=4
+ * @license amdefine 1.0.0 Copyright (c) 2011-2015, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/amdefine for details
+ */
+
+/*jslint node: true */
+/*global module, process */
+'use strict';
+
+/**
+ * Creates a define for node.
+ * @param {Object} module the "module" object that is defined by Node for the
+ * current module.
+ * @param {Function} [requireFn]. Node's require function for the current module.
+ * It only needs to be passed in Node versions before 0.5, when module.require
+ * did not exist.
+ * @returns {Function} a define function that is usable for the current node
+ * module.
+ */
+function amdefine(module, requireFn) {
+    'use strict';
+    var defineCache = {},
+        loaderCache = {},
+        alreadyCalled = false,
+        path = require('path'),
+        makeRequire, stringRequire;
+
+    /**
+     * Trims the . and .. from an array of path segments.
+     * It will keep a leading path segment if a .. will become
+     * the first path segment, to help with module name lookups,
+     * which act like paths, but can be remapped. But the end result,
+     * all paths that use this function should look normalized.
+     * NOTE: this method MODIFIES the input array.
+     * @param {Array} ary the array of path segments.
+     */
+    function trimDots(ary) {
+        var i, part;
+        for (i = 0; ary[i]; i+= 1) {
+            part = ary[i];
+            if (part === '.') {
+                ary.splice(i, 1);
+                i -= 1;
+            } else if (part === '..') {
+                if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+                    //End of the line. Keep at least one non-dot
+                    //path segment at the front so it can be mapped
+                    //correctly to disk. Otherwise, there is likely
+                    //no path mapping for a path starting with '..'.
+                    //This can still fail, but catches the most reasonable
+                    //uses of ..
+                    break;
+                } else if (i > 0) {
+                    ary.splice(i - 1, 2);
+                    i -= 2;
+                }
+            }
+        }
+    }
+
+    function normalize(name, baseName) {
+        var baseParts;
+
+        //Adjust any relative paths.
+        if (name && name.charAt(0) === '.') {
+            //If have a base name, try to normalize against it,
+            //otherwise, assume it is a top-level require that will
+            //be relative to baseUrl in the end.
+            if (baseName) {
+                baseParts = baseName.split('/');
+                baseParts = baseParts.slice(0, baseParts.length - 1);
+                baseParts = baseParts.concat(name.split('/'));
+                trimDots(baseParts);
+                name = baseParts.join('/');
+            }
+        }
+
+        return name;
+    }
+
+    /**
+     * Create the normalize() function passed to a loader plugin's
+     * normalize method.
+     */
+    function makeNormalize(relName) {
+        return function (name) {
+            return normalize(name, relName);
+        };
+    }
+
+    function makeLoad(id) {
+        function load(value) {
+            loaderCache[id] = value;
+        }
+
+        load.fromText = function (id, text) {
+            //This one is difficult because the text can/probably uses
+            //define, and any relative paths and requires should be relative
+            //to that id was it would be found on disk. But this would require
+            //bootstrapping a module/require fairly deeply from node core.
+            //Not sure how best to go about that yet.
+            throw new Error('amdefine does not implement load.fromText');
+        };
+
+        return load;
+    }
+
+    makeRequire = function (systemRequire, exports, module, relId) {
+        function amdRequire(deps, callback) {
+            if (typeof deps === 'string') {
+                //Synchronous, single module require('')
+                return stringRequire(systemRequire, exports, module, deps, relId);
+            } else {
+                //Array of dependencies with a callback.
+
+                //Convert the dependencies to modules.
+                deps = deps.map(function (depName) {
+                    return stringRequire(systemRequire, exports, module, depName, relId);
+                });
+
+                //Wait for next tick to call back the require call.
+                if (callback) {
+                    process.nextTick(function () {
+                        callback.apply(null, deps);
+                    });
+                }
+            }
+        }
+
+        amdRequire.toUrl = function (filePath) {
+            if (filePath.indexOf('.') === 0) {
+                return normalize(filePath, path.dirname(module.filename));
+            } else {
+                return filePath;
+            }
+        };
+
+        return amdRequire;
+    };
+
+    //Favor explicit value, passed in if the module wants to support Node 0.4.
+    requireFn = requireFn || function req() {
+        return module.require.apply(module, arguments);
+    };
+
+    function runFactory(id, deps, factory) {
+        var r, e, m, result;
+
+        if (id) {
+            e = loaderCache[id] = {};
+            m = {
+                id: id,
+                uri: __filename,
+                exports: e
+            };
+            r = makeRequire(requireFn, e, m, id);
+        } else {
+            //Only support one define call per file
+            if (alreadyCalled) {
+                throw new Error('amdefine with no module ID cannot be called more than once per file.');
+            }
+            alreadyCalled = true;
+
+            //Use the real variables from node
+            //Use module.exports for exports, since
+            //the exports in here is amdefine exports.
+            e = module.exports;
+            m = module;
+            r = makeRequire(requireFn, e, m, module.id);
+        }
+
+        //If there are dependencies, they are strings, so need
+        //to convert them to dependency values.
+        if (deps) {
+            deps = deps.map(function (depName) {
+                return r(depName);
+            });
+        }
+
+        //Call the factory with the right dependencies.
+        if (typeof factory === 'function') {
+            result = factory.apply(m.exports, deps);
+        } else {
+            result = factory;
+        }
+
+        if (result !== undefined) {
+            m.exports = result;
+            if (id) {
+                loaderCache[id] = m.exports;
+            }
+        }
+    }
+
+    stringRequire = function (systemRequire, exports, module, id, relId) {
+        //Split the ID by a ! so that
+        var index = id.indexOf('!'),
+            originalId = id,
+            prefix, plugin;
+
+        if (index === -1) {
+            id = normalize(id, relId);
+
+            //Straight module lookup. If it is one of the special dependencies,
+            //deal with it, otherwise, delegate to node.
+            if (id === 'require') {
+                return makeRequire(systemRequire, exports, module, relId);
+            } else if (id === 'exports') {
+                return exports;
+            } else if (id === 'module') {
+                return module;
+            } else if (loaderCache.hasOwnProperty(id)) {
+                return loaderCache[id];
+            } else if (defineCache[id]) {
+                runFactory.apply(null, defineCache[id]);
+                return loaderCache[id];
+            } else {
+                if(systemRequire) {
+                    return systemRequire(originalId);
+                } else {
+                    throw new Error('No module with ID: ' + id);
+                }
+            }
+        } else {
+            //There is a plugin in play.
+            prefix = id.substring(0, index);
+            id = id.substring(index + 1, id.length);
+
+            plugin = stringRequire(systemRequire, exports, module, prefix, relId);
+
+            if (plugin.normalize) {
+                id = plugin.normalize(id, makeNormalize(relId));
+            } else {
+                //Normalize the ID normally.
+                id = normalize(id, relId);
+            }
+
+            if (loaderCache[id]) {
+                return loaderCache[id];
+            } else {
+                plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
+
+                return loaderCache[id];
+            }
+        }
+    };
+
+    //Create a define function specific to the module asking for amdefine.
+    function define(id, deps, factory) {
+        if (Array.isArray(id)) {
+            factory = deps;
+            deps = id;
+            id = undefined;
+        } else if (typeof id !== 'string') {
+            factory = id;
+            id = deps = undefined;
+        }
+
+        if (deps && !Array.isArray(deps)) {
+            factory = deps;
+            deps = undefined;
+        }
+
+        if (!deps) {
+            deps = ['require', 'exports', 'module'];
+        }
+
+        //Set up properties for this module. If an ID, then use
+        //internal cache. If no ID, then use the external variables
+        //for this node module.
+        if (id) {
+            //Put the module in deep freeze until there is a
+            //require call for it.
+            defineCache[id] = [id, deps, factory];
+        } else {
+            runFactory(id, deps, factory);
+        }
+    }
+
+    //define.require, which has access to all the values in the
+    //cache. Useful for AMD modules that all have IDs in the file,
+    //but need to finally export a value to node based on one of those
+    //IDs.
+    define.require = function (id) {
+        if (loaderCache[id]) {
+            return loaderCache[id];
+        }
+
+        if (defineCache[id]) {
+            runFactory.apply(null, defineCache[id]);
+            return loaderCache[id];
+        }
+    };
+
+    define.amd = {};
+
+    return define;
+}
+
+module.exports = amdefine;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/intercept.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/intercept.js
new file mode 100644
index 0000000..771a983
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/intercept.js
@@ -0,0 +1,36 @@
+/*jshint node: true */
+var inserted,
+    Module = require('module'),
+    fs = require('fs'),
+    existingExtFn = Module._extensions['.js'],
+    amdefineRegExp = /amdefine\.js/;
+
+inserted  = "if (typeof define !== 'function') {var define = require('amdefine')(module)}";
+
+//From the node/lib/module.js source:
+function stripBOM(content) {
+    // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
+    // because the buffer-to-string conversion in `fs.readFileSync()`
+    // translates it to FEFF, the UTF-16 BOM.
+    if (content.charCodeAt(0) === 0xFEFF) {
+        content = content.slice(1);
+    }
+    return content;
+}
+
+//Also adapted from the node/lib/module.js source:
+function intercept(module, filename) {
+    var content = stripBOM(fs.readFileSync(filename, 'utf8'));
+
+    if (!amdefineRegExp.test(module.id)) {
+        content = inserted + content;
+    }
+
+    module._compile(content, filename);
+}
+
+intercept._id = 'amdefine/intercept';
+
+if (!existingExtFn._id || existingExtFn._id !== intercept._id) {
+    Module._extensions['.js'] = intercept;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/package.json
new file mode 100644
index 0000000..897ee44
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/node_modules/amdefine/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "amdefine",
+  "description": "Provide AMD's define() API for declaring modules in the AMD format",
+  "version": "1.0.0",
+  "homepage": "http://github.com/jrburke/amdefine",
+  "author": {
+    "name": "James Burke",
+    "email": "jrburke@gmail.com",
+    "url": "http://github.com/jrburke"
+  },
+  "license": "BSD-3-Clause AND MIT",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jrburke/amdefine.git"
+  },
+  "main": "./amdefine.js",
+  "engines": {
+    "node": ">=0.4.2"
+  },
+  "gitHead": "578bc4a3f7dede33f3f3e10edde0c1607005d761",
+  "bugs": {
+    "url": "https://github.com/jrburke/amdefine/issues"
+  },
+  "_id": "amdefine@1.0.0",
+  "scripts": {},
+  "_shasum": "fd17474700cb5cc9c2b709f0be9d23ce3c198c33",
+  "_from": "amdefine@>=0.0.4",
+  "_npmVersion": "2.12.1",
+  "_nodeVersion": "0.10.36",
+  "_npmUser": {
+    "name": "jrburke",
+    "email": "jrburke@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "jrburke",
+      "email": "jrburke@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "fd17474700cb5cc9c2b709f0be9d23ce3c198c33",
+    "tarball": "http://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/package.json
new file mode 100644
index 0000000..537c798
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/node_modules/source-map/package.json
@@ -0,0 +1,201 @@
+{
+  "name": "source-map",
+  "description": "Generates and consumes source maps",
+  "version": "0.4.4",
+  "homepage": "https://github.com/mozilla/source-map",
+  "author": {
+    "name": "Nick Fitzgerald",
+    "email": "nfitzgerald@mozilla.com"
+  },
+  "contributors": [
+    {
+      "name": "Tobias Koppers",
+      "email": "tobias.koppers@googlemail.com"
+    },
+    {
+      "name": "Duncan Beevers",
+      "email": "duncan@dweebd.com"
+    },
+    {
+      "name": "Stephen Crane",
+      "email": "scrane@mozilla.com"
+    },
+    {
+      "name": "Ryan Seddon",
+      "email": "seddon.ryan@gmail.com"
+    },
+    {
+      "name": "Miles Elam",
+      "email": "miles.elam@deem.com"
+    },
+    {
+      "name": "Mihai Bazon",
+      "email": "mihai.bazon@gmail.com"
+    },
+    {
+      "name": "Michael Ficarra",
+      "email": "github.public.email@michael.ficarra.me"
+    },
+    {
+      "name": "Todd Wolfson",
+      "email": "todd@twolfson.com"
+    },
+    {
+      "name": "Alexander Solovyov",
+      "email": "alexander@solovyov.net"
+    },
+    {
+      "name": "Felix Gnass",
+      "email": "fgnass@gmail.com"
+    },
+    {
+      "name": "Conrad Irwin",
+      "email": "conrad.irwin@gmail.com"
+    },
+    {
+      "name": "usrbincc",
+      "email": "usrbincc@yahoo.com"
+    },
+    {
+      "name": "David Glasser",
+      "email": "glasser@davidglasser.net"
+    },
+    {
+      "name": "Chase Douglas",
+      "email": "chase@newrelic.com"
+    },
+    {
+      "name": "Evan Wallace",
+      "email": "evan.exe@gmail.com"
+    },
+    {
+      "name": "Heather Arthur",
+      "email": "fayearthur@gmail.com"
+    },
+    {
+      "name": "Hugh Kennedy",
+      "email": "hughskennedy@gmail.com"
+    },
+    {
+      "name": "David Glasser",
+      "email": "glasser@davidglasser.net"
+    },
+    {
+      "name": "Simon Lydell",
+      "email": "simon.lydell@gmail.com"
+    },
+    {
+      "name": "Jmeas Smith",
+      "email": "jellyes2@gmail.com"
+    },
+    {
+      "name": "Michael Z Goddard",
+      "email": "mzgoddard@gmail.com"
+    },
+    {
+      "name": "azu",
+      "email": "azu@users.noreply.github.com"
+    },
+    {
+      "name": "John Gozde",
+      "email": "john@gozde.ca"
+    },
+    {
+      "name": "Adam Kirkton",
+      "email": "akirkton@truefitinnovation.com"
+    },
+    {
+      "name": "Chris Montgomery",
+      "email": "christopher.montgomery@dowjones.com"
+    },
+    {
+      "name": "J. Ryan Stinnett",
+      "email": "jryans@gmail.com"
+    },
+    {
+      "name": "Jack Herrington",
+      "email": "jherrington@walmartlabs.com"
+    },
+    {
+      "name": "Chris Truter",
+      "email": "jeffpalentine@gmail.com"
+    },
+    {
+      "name": "Daniel Espeset",
+      "email": "daniel@danielespeset.com"
+    },
+    {
+      "name": "Jamie Wong",
+      "email": "jamie.lf.wong@gmail.com"
+    },
+    {
+      "name": "Eddy Bruël",
+      "email": "ejpbruel@mozilla.com"
+    },
+    {
+      "name": "Hawken Rives",
+      "email": "hawkrives@gmail.com"
+    },
+    {
+      "name": "Gilad Peleg",
+      "email": "giladp007@gmail.com"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/mozilla/source-map.git"
+  },
+  "directories": {
+    "lib": "./lib"
+  },
+  "main": "./lib/source-map.js",
+  "files": [
+    "lib/",
+    "build/"
+  ],
+  "engines": {
+    "node": ">=0.8.0"
+  },
+  "license": "BSD-3-Clause",
+  "dependencies": {
+    "amdefine": ">=0.0.4"
+  },
+  "devDependencies": {
+    "dryice": ">=0.4.8"
+  },
+  "scripts": {
+    "test": "node test/run-tests.js",
+    "build": "node Makefile.dryice.js"
+  },
+  "bugs": {
+    "url": "https://github.com/mozilla/source-map/issues"
+  },
+  "_id": "source-map@0.4.4",
+  "_shasum": "eba4f5da9c0dc999de68032d8b4f76173652036b",
+  "_from": "source-map@>=0.4.0 <0.5.0",
+  "_npmVersion": "1.4.9",
+  "_npmUser": {
+    "name": "nickfitzgerald",
+    "email": "fitzgen@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "mozilla-devtools",
+      "email": "mozilla-developer-tools@googlegroups.com"
+    },
+    {
+      "name": "mozilla",
+      "email": "dherman@mozilla.com"
+    },
+    {
+      "name": "nickfitzgerald",
+      "email": "fitzgen@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "eba4f5da9c0dc999de68032d8b4f76173652036b",
+    "tarball": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz"
+  },
+  "_resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/package.json
new file mode 100644
index 0000000..553ea73
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/clean-css/package.json
@@ -0,0 +1,83 @@
+{
+  "name": "clean-css",
+  "version": "3.4.1",
+  "author": {
+    "name": "Jakub Pawlowicz",
+    "email": "contact@jakubpawlowicz.com",
+    "url": "http://twitter.com/jakubpawlowicz"
+  },
+  "description": "A well-tested CSS minifier",
+  "license": "MIT",
+  "keywords": [
+    "css",
+    "minifier"
+  ],
+  "homepage": "https://github.com/jakubpawlowicz/clean-css",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jakubpawlowicz/clean-css.git"
+  },
+  "bugs": {
+    "url": "https://github.com/jakubpawlowicz/clean-css/issues"
+  },
+  "bin": {
+    "cleancss": "./bin/cleancss"
+  },
+  "main": "index.js",
+  "files": [
+    "bin",
+    "lib",
+    "History.md",
+    "index.js",
+    "LICENSE"
+  ],
+  "scripts": {
+    "browserify": "browserify --standalone CleanCSS index.js | uglifyjs --compress --mangle -o cleancss-browser.js",
+    "bench": "node ./test/bench.js",
+    "check": "jshint ./bin/cleancss .",
+    "prepublish": "npm run check",
+    "test": "vows"
+  },
+  "dependencies": {
+    "commander": "2.8.x",
+    "source-map": "0.4.x"
+  },
+  "devDependencies": {
+    "browserify": "11.x",
+    "http-proxy": "1.x",
+    "jshint": "2.8.x",
+    "nock": "2.x",
+    "server-destroy": "1.x",
+    "uglify-js": "2.4.x",
+    "vows": "0.8.x"
+  },
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "gitHead": "20f029a0fc33a01c3d0865576f34e520c2cdb36f",
+  "_id": "clean-css@3.4.1",
+  "_shasum": "632b753182d35e365bea8ce90625a99d83a82e2c",
+  "_from": "clean-css@>=3.3.0 <4.0.0",
+  "_npmVersion": "2.5.1",
+  "_nodeVersion": "0.12.0",
+  "_npmUser": {
+    "name": "goalsmashers",
+    "email": "jakub@goalsmashers.com"
+  },
+  "dist": {
+    "shasum": "632b753182d35e365bea8ce90625a99d83a82e2c",
+    "tarball": "http://registry.npmjs.org/clean-css/-/clean-css-3.4.1.tgz"
+  },
+  "maintainers": [
+    {
+      "name": "goalsmashers",
+      "email": "jakub@goalsmashers.com"
+    },
+    {
+      "name": "jakub.pawlowicz",
+      "email": "contact@jakubpawlowicz.com"
+    }
+  ],
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.1.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.gitattributes b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.gitattributes
new file mode 100644
index 0000000..412eeda
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.gitattributes
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.npmignore
new file mode 100644
index 0000000..b83202d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.npmignore
@@ -0,0 +1,13 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/LICENSE
new file mode 100644
index 0000000..35cc606
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/README.md
new file mode 100644
index 0000000..506f7ea
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/README.md
@@ -0,0 +1,42 @@
+# constantinople
+
+Determine whether a JavaScript expression evaluates to a constant (using acorn).  Here it is assumed to be safe to underestimate how constant something is.
+
+[![Build Status](https://img.shields.io/travis/ForbesLindesay/constantinople/master.svg)](https://travis-ci.org/ForbesLindesay/constantinople)
+[![Dependency Status](https://img.shields.io/gemnasium/ForbesLindesay/constantinople.svg)](https://gemnasium.com/ForbesLindesay/constantinople)
+[![NPM version](https://img.shields.io/npm/v/constantinople.svg)](https://www.npmjs.org/package/constantinople)
+
+## Installation
+
+    npm install constantinople
+
+## Usage
+
+```js
+var isConstant = require('constantinople')
+
+if (isConstant('"foo" + 5')) {
+  console.dir(isConstant.toConstant('"foo" + 5'))
+}
+if (isConstant('Math.floor(10.5)', {Math: Math})) {
+  console.dir(isConstant.toConstant('Math.floor(10.5)', {Math: Math}))
+}
+```
+
+## API
+
+### isConstant(src, [constants])
+
+Returns `true` if `src` evaluates to a constant, `false` otherwise.  It will also return `false` if there is a syntax error, which makes it safe to use on potentially ES6 code.
+
+Constants is an object mapping strings to values, where those values should be treated as constants.  Note that this makes it a pretty bad idea to have `Math` in there if the user might make use of `Math.random` and a pretty bad idea to have `Date` in there.
+
+### toConstant(src, [constants])
+
+Returns the value resulting from evaluating `src`.  This method throws an error if the expression is not constant.  e.g. `toConstant("Math.random()")` would throw an error.
+
+Constants is an object mapping strings to values, where those values should be treated as constants.  Note that this makes it a pretty bad idea to have `Math` in there if the user might make use of `Math.random` and a pretty bad idea to have `Date` in there.
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/index.js
new file mode 100644
index 0000000..6a2f2f5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/index.js
@@ -0,0 +1,100 @@
+'use strict'
+
+var acorn = require('acorn');
+var walk = require('acorn/dist/walk');
+
+var lastSRC = '(null)';
+var lastRes = true;
+var lastConstants = undefined;
+
+var STATEMENT_WHITE_LIST = {
+  'EmptyStatement': true,
+  'ExpressionStatement': true,
+};
+var EXPRESSION_WHITE_LIST = {
+  'ParenthesizedExpression': true,
+  'ArrayExpression': true,
+  'ObjectExpression': true,
+  'SequenceExpression': true,
+  'TemplateLiteral': true,
+  'UnaryExpression': true,
+  'BinaryExpression': true,
+  'LogicalExpression': true,
+  'ConditionalExpression': true,
+  'Identifier': true,
+  'Literal': true,
+  'ComprehensionExpression': true,
+  'TaggedTemplateExpression': true,
+  'MemberExpression': true,
+  'CallExpression': true,
+  'NewExpression': true,
+};
+module.exports = isConstant;
+function isConstant(src, constants) {
+  src = '(' + src + ')';
+  if (lastSRC === src && lastConstants === constants) return lastRes;
+  lastSRC = src;
+  lastConstants = constants;
+  if (!isExpression(src)) return lastRes = false;
+  var ast;
+  try {
+    ast = acorn.parse(src, {
+      ecmaVersion: 6,
+      allowReturnOutsideFunction: true,
+      allowImportExportEverywhere: true,
+      allowHashBang: true
+    });
+  } catch (ex) {
+    return lastRes = false;
+  }
+  var isConstant = true;
+  walk.simple(ast, {
+    Statement: function (node) {
+      if (isConstant) {
+        if (STATEMENT_WHITE_LIST[node.type] !== true) {
+          isConstant = false;
+        }
+      }
+    },
+    Expression: function (node) {
+      if (isConstant) {
+        if (EXPRESSION_WHITE_LIST[node.type] !== true) {
+          isConstant = false;
+        }
+      }
+    },
+    MemberExpression: function (node) {
+      if (isConstant) {
+        if (node.computed) isConstant = false;
+        else if (node.property.name[0] === '_') isConstant = false;
+      }
+    },
+    Identifier: function (node) {
+      if (isConstant) {
+        if (!constants || !(node.name in constants)) {
+          isConstant = false;
+        }
+      }
+    },
+  });
+  return lastRes = isConstant;
+}
+isConstant.isConstant = isConstant;
+
+isConstant.toConstant = toConstant;
+function toConstant(src, constants) {
+  if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
+  return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
+    return constants[key];
+  }));
+}
+
+function isExpression(src) {
+  try {
+    eval('throw "STOP"; (function () { return (' + src + '); })()');
+    return false;
+  }
+  catch (err) {
+    return err === 'STOP';
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/.bin/acorn b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/.bin/acorn
new file mode 100644
index 0000000..261da85
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/.bin/acorn
@@ -0,0 +1,15 @@
+#!/bin/sh
+basedir=`dirname "$0"`
+
+case `uname` in
+    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  "$basedir/node"  "$basedir/../acorn/bin/acorn" "$@"
+  ret=$?
+else 
+  node  "$basedir/../acorn/bin/acorn" "$@"
+  ret=$?
+fi
+exit $ret
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/.bin/acorn.cmd b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/.bin/acorn.cmd
new file mode 100644
index 0000000..45c0c3d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/.bin/acorn.cmd
@@ -0,0 +1,7 @@
+@IF EXIST "%~dp0\node.exe" (
+  "%~dp0\node.exe"  "%~dp0\..\acorn\bin\acorn" %*
+) ELSE (
+  @SETLOCAL
+  @SET PATHEXT=%PATHEXT:;.JS;=;%
+  node  "%~dp0\..\acorn\bin\acorn" %*
+)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.editorconfig b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.editorconfig
new file mode 100644
index 0000000..c14d5c6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.gitattributes b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.gitattributes
new file mode 100644
index 0000000..fcadb2c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.gitattributes
@@ -0,0 +1 @@
+* text eol=lf
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.npmignore
new file mode 100644
index 0000000..ecba291
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.npmignore
@@ -0,0 +1,3 @@
+/.tern-port
+/test
+/local
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.tern-project b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.tern-project
new file mode 100644
index 0000000..6718ce0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.tern-project
@@ -0,0 +1,6 @@
+{
+  "plugins": {
+    "node": true,
+    "es_modules": true
+  }
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.travis.yml
new file mode 100644
index 0000000..ffb9f71
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/.travis.yml
@@ -0,0 +1,2 @@
+language: node_js
+node_js: '0.10'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/AUTHORS b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/AUTHORS
new file mode 100644
index 0000000..0e8f48b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/AUTHORS
@@ -0,0 +1,43 @@
+List of Acorn contributors. Updated before every release.
+
+Adrian Rakovsky
+Alistair Braidwood
+Andres Suarez
+Aparajita Fishman
+Arian Stolwijk
+Artem Govorov
+Brandon Mills
+Charles Hughes
+Conrad Irwin
+David Bonnet
+ForbesLindesay
+Forbes Lindesay
+Gilad Peleg
+impinball
+Ingvar Stepanyan
+Jesse McCarthy
+Jiaxing Wang
+Joel Kemp
+Johannes Herr
+Jürg Lehni
+keeyipchan
+Kevin Kwok
+krator
+Marijn Haverbeke
+Martin Carlberg
+Mathias Bynens
+Mathieu 'p01' Henri
+Max Schaefer
+Max Zerzouri
+Mihai Bazon
+Mike Rennie
+Nick Fitzgerald
+Oskar Schöldström
+Paul Harper
+Peter Rust
+PlNG
+r-e-d
+Rich Harris
+Sebastian McKenzie
+Timothy Gu
+zsjforcn
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/LICENSE
new file mode 100644
index 0000000..d4c7fc5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2012-2014 by various contributors (see AUTHORS)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/README.md
new file mode 100644
index 0000000..5c58142
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/README.md
@@ -0,0 +1,386 @@
+# Acorn
+
+[![Build Status](https://travis-ci.org/marijnh/acorn.svg?branch=master)](https://travis-ci.org/marijnh/acorn)
+[![NPM version](https://img.shields.io/npm/v/acorn.svg)](https://www.npmjs.org/package/acorn)  
+[Author funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?force)](https://marijnhaverbeke.nl/fund/)
+
+A tiny, fast JavaScript parser, written completely in JavaScript.
+
+## Installation
+
+The easiest way to install acorn is with [`npm`][npm].
+
+[npm]: http://npmjs.org
+
+```sh
+npm install acorn
+```
+
+Alternately, download the source.
+
+```sh
+git clone https://github.com/marijnh/acorn.git
+```
+
+## Components
+
+When run in a CommonJS (node.js) or AMD environment, exported values
+appear in the interfaces exposed by the individual files, as usual.
+When loaded in the browser (Acorn works in any JS-enabled browser more
+recent than IE5) without any kind of module management, a single
+global object `acorn` will be defined, and all the exported properties
+will be added to that.
+
+### Main parser
+
+This is implemented in `dist/acorn.js`, and is what you get when you
+`require("acorn")` in node.js.
+
+**parse**`(input, options)` is used to parse a JavaScript program.
+The `input` parameter is a string, `options` can be undefined or an
+object setting some of the options listed below. The return value will
+be an abstract syntax tree object as specified by the
+[ESTree spec][estree].
+
+When  encountering   a  syntax   error,  the   parser  will   raise  a
+`SyntaxError` object with a meaningful  message. The error object will
+have a `pos` property that indicates the character offset at which the
+error occurred,  and a `loc`  object that contains a  `{line, column}`
+object referring to that same position.
+
+[estree]: https://github.com/estree/estree
+
+- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
+  either 3, 5, or 6. This influences support for strict mode, the set
+  of reserved words, and support for new syntax features. Default is 5.
+
+- **sourceType**: Indicate the mode the code should be parsed in. Can be
+  either `"script"` or `"module"`.
+
+- **onInsertedSemicolon**: If given a callback, that callback will be
+  called whenever a missing semicolon is inserted by the parser. The
+  callback will be given the character offset of the point where the
+  semicolon is inserted as argument, and if `locations` is on, also a
+  `{line, column}` object representing this position.
+
+- **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing
+  commas.
+
+- **allowReserved**: If `false`, using a reserved word will generate
+  an error. Defaults to `true`. When given the value `"never"`,
+  reserved words and keywords can also not be used as property names
+  (as in Internet Explorer's old parser).
+
+- **allowReturnOutsideFunction**: By default, a return statement at
+  the top level raises an error. Set this to `true` to accept such
+  code.
+
+- **allowImportExportEverywhere**: By default, `import` and `export`
+  declarations can only appear at a program's top level. Setting this
+  option to `true` allows them anywhere where a statement is allowed.
+
+- **allowHashBang**: When this is enabled (off by default), if the
+  code starts with the characters `#!` (as in a shellscript), the
+  first line will be treated as a comment.
+
+- **locations**: When `true`, each node has a `loc` object attached
+  with `start` and `end` subobjects, each of which contains the
+  one-based line and zero-based column numbers in `{line, column}`
+  form. Default is `false`.
+
+- **onToken**: If a function is passed for this option, each found
+  token will be passed in same format as `tokenize()` returns.
+
+  If array is passed, each found token is pushed to it.
+
+  Note that you are not allowed to call the parser from the
+  callback—that will corrupt its internal state.
+
+- **onComment**: If a function is passed for this option, whenever a
+  comment is encountered the function will be called with the
+  following parameters:
+
+  - `block`: `true` if the comment is a block comment, false if it
+    is a line comment.
+  - `text`: The content of the comment.
+  - `start`: Character offset of the start of the comment.
+  - `end`: Character offset of the end of the comment.
+
+  When the `locations` options is on, the `{line, column}` locations
+  of the comment’s start and end are passed as two additional
+  parameters.
+
+  If array is passed for this option, each found comment is pushed
+  to it as object in Esprima format:
+
+  ```javascript
+  {
+    "type": "Line" | "Block",
+    "value": "comment text",
+    "start": Number,
+    "end": Number,
+    // If `locations` option is on:
+    "loc": {
+      "start": {line: Number, column: Number}
+      "end": {line: Number, column: Number}
+    },
+    // If `ranges` option is on:
+    "range": [Number, Number]
+  }
+  ```
+
+  Note that you are not allowed to call the parser from the
+  callback—that will corrupt its internal state.
+
+- **ranges**: Nodes have their start and end characters offsets
+  recorded in `start` and `end` properties (directly on the node,
+  rather than the `loc` object, which holds line/column data. To also
+  add a [semi-standardized][range] `range` property holding a
+  `[start, end]` array with the same numbers, set the `ranges` option
+  to `true`.
+
+- **program**: It is possible to parse multiple files into a single
+  AST by passing the tree produced by parsing the first file as the
+  `program` option in subsequent parses. This will add the toplevel
+  forms of the parsed file to the "Program" (top) node of an existing
+  parse tree.
+
+- **sourceFile**: When the `locations` option is `true`, you can pass
+  this option to add a `source` attribute in every node’s `loc`
+  object. Note that the contents of this option are not examined or
+  processed in any way; you are free to use whatever format you
+  choose.
+
+- **directSourceFile**: Like `sourceFile`, but a `sourceFile` property
+  will be added directly to the nodes, rather than the `loc` object.
+
+- **preserveParens**: If this option is `true`, parenthesized expressions
+  are represented by (non-standard) `ParenthesizedExpression` nodes
+  that have a single `expression` property containing the expression
+  inside parentheses.
+
+[range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
+
+**parseExpressionAt**`(input, offset, options)` will parse a single
+expression in a string, and return its AST. It will not complain if
+there is more of the string left after the expression.
+
+**getLineInfo**`(input, offset)` can be used to get a `{line,
+column}` object for a given program string and character offset.
+
+**tokenizer**`(input, options)` returns an object with a `getToken`
+method that can be called repeatedly to get the next token, a `{start,
+end, type, value}` object (with added `loc` property when the
+`locations` option is enabled and `range` property when the `ranges`
+option is enabled). When the token's type is `tokTypes.eof`, you
+should stop calling the method, since it will keep returning that same
+token forever.
+
+In ES6 environment, returned result can be used as any other
+protocol-compliant iterable:
+
+```javascript
+for (let token of acorn.tokenize(str)) {
+  // iterate over the tokens
+}
+
+// transform code to array of tokens:
+var tokens = [...acorn.tokenize(str)];
+```
+
+**tokTypes** holds an object mapping names to the token type objects
+that end up in the `type` properties of tokens.
+
+#### Note on using with [Escodegen][escodegen]
+
+Escodegen supports generating comments from AST, attached in
+Esprima-specific format. In order to simulate same format in
+Acorn, consider following example:
+
+```javascript
+var comments = [], tokens = [];
+
+var ast = acorn.parse('var x = 42; // answer', {
+	// collect ranges for each node
+	ranges: true,
+	// collect comments in Esprima's format
+	onComment: comments,
+	// collect token ranges
+	onToken: tokens
+});
+
+// attach comments using collected information
+escodegen.attachComments(ast, comments, tokens);
+
+// generate code
+console.log(escodegen.generate(ast, {comment: true}));
+// > 'var x = 42;    // answer'
+```
+
+[escodegen]: https://github.com/Constellation/escodegen
+
+#### Using Acorn in an environment with a Content Security Policy
+
+Some contexts, such as Chrome Web Apps, disallow run-time code evaluation.
+Acorn uses `new Function` to generate fast functions that test whether
+a word is in a given set, and will trigger a security error when used
+in a context with such a
+[Content Security Policy](http://www.html5rocks.com/en/tutorials/security/content-security-policy/#eval-too)
+(see [#90](https://github.com/marijnh/acorn/issues/90) and
+[#123](https://github.com/marijnh/acorn/issues/123)).
+
+The `dist/acorn_csp.js` file in the distribution (which is built
+by the `bin/without_eval` script) has the generated code inlined, and
+can thus run without evaluating anything.
+
+### dist/acorn_loose.js ###
+
+This file implements an error-tolerant parser. It exposes a single
+function. The loose parser is accessible in node.js via `require("acorn/dist/acorn_loose")`.
+
+**parse_dammit**`(input, options)` takes the same arguments and
+returns the same syntax tree as the `parse` function in `acorn.js`,
+but never raises an error, and will do its best to parse syntactically
+invalid code in as meaningful a way as it can. It'll insert identifier
+nodes with name `"✖"` as placeholders in places where it can't make
+sense of the input. Depends on `acorn.js`, because it uses the same
+tokenizer.
+
+### dist/walk.js ###
+
+Implements an abstract syntax tree walker. Will store its interface in
+`acorn.walk` when loaded without a module system.
+
+**simple**`(node, visitors, base, state)` does a 'simple' walk over
+a tree. `node` should be the AST node to walk, and `visitors` an
+object with properties whose names correspond to node types in the
+[ESTree spec][estree]. The properties should contain functions
+that will be called with the node object and, if applicable the state
+at that point. The last two arguments are optional. `base` is a walker
+algorithm, and `state` is a start state. The default walker will
+simply visit all statements and expressions and not produce a
+meaningful state. (An example of a use of state it to track scope at
+each point in the tree.)
+
+**ancestor**`(node, visitors, base, state)` does a 'simple' walk over
+a tree, building up an array of ancestor nodes (including the current node)
+and passing the array to callbacks in the `state` parameter.
+
+**recursive**`(node, state, functions, base)` does a 'recursive'
+walk, where the walker functions are responsible for continuing the
+walk on the child nodes of their target node. `state` is the start
+state, and `functions` should contain an object that maps node types
+to walker functions. Such functions are called with `(node, state, c)`
+arguments, and can cause the walk to continue on a sub-node by calling
+the `c` argument on it with `(node, state)` arguments. The optional
+`base` argument provides the fallback walker functions for node types
+that aren't handled in the `functions` object. If not given, the
+default walkers will be used.
+
+**make**`(functions, base)` builds a new walker object by using the
+walker functions in `functions` and filling in the missing ones by
+taking defaults from `base`.
+
+**findNodeAt**`(node, start, end, test, base, state)` tries to
+locate a node in a tree at the given start and/or end offsets, which
+satisfies the predicate `test`. `start` end `end` can be either `null`
+(as wildcard) or a number. `test` may be a string (indicating a node
+type) or a function that takes `(nodeType, node)` arguments and
+returns a boolean indicating whether this node is interesting. `base`
+and `state` are optional, and can be used to specify a custom walker.
+Nodes are tested from inner to outer, so if two nodes match the
+boundaries, the inner one will be preferred.
+
+**findNodeAround**`(node, pos, test, base, state)` is a lot like
+`findNodeAt`, but will match any node that exists 'around' (spanning)
+the given position.
+
+**findNodeAfter**`(node, pos, test, base, state)` is similar to
+`findNodeAround`, but will match all nodes *after* the given position
+(testing outer nodes before inner nodes).
+
+## Command line interface
+
+The `bin/acorn` utility can be used to parse a file from the command
+line. It accepts as arguments its input file and the following
+options:
+
+- `--ecma3|--ecma5|--ecma6`: Sets the ECMAScript version to parse. Default is
+  version 5.
+
+- `--module`: Sets the parsing mode to `"module"`. Is set to `"script"` otherwise.
+
+- `--locations`: Attaches a "loc" object to each node with "start" and
+  "end" subobjects, each of which contains the one-based line and
+  zero-based column numbers in `{line, column}` form.
+
+- `--allow-hash-bang`: If the code starts with the characters #! (as in a shellscript), the first line will be treated as a comment.
+
+- `--compact`: No whitespace is used in the AST output.
+
+- `--silent`: Do not output the AST, just return the exit status.
+
+- `--help`: Print the usage information and quit.
+
+The utility spits out the syntax tree as JSON data.
+
+## Build system
+
+Acorn is written in ECMAScript 6, as a set of small modules, in the
+project's `src` directory, and compiled down to bigger ECMAScript 3
+files in `dist` using [Browserify](http://browserify.org) and
+[Babel](http://babeljs.io/). If you are already using Babel, you can
+consider including the modules directly.
+
+The command-line test runner (`npm test`) uses the ES6 modules. The
+browser-based test page (`test/index.html`) uses the compiled modules.
+The `bin/build-acorn.js` script builds the latter from the former.
+
+If you are working on Acorn, you'll probably want to try the code out
+directly, without an intermediate build step. In your scripts, you can
+register the Babel require shim like this:
+
+    require("babel-core/register")
+
+That will allow you to directly `require` the ES6 modules.
+
+## Plugins
+
+Acorn is designed support allow plugins which, within reasonable
+bounds, redefine the way the parser works. Plugins can add new token
+types and new tokenizer contexts (if necessary), and extend methods in
+the parser object. This is not a clean, elegant API—using it requires
+an understanding of Acorn's internals, and plugins are likely to break
+whenever those internals are significantly changed. But still, it is
+_possible_, in this way, to create parsers for JavaScript dialects
+without forking all of Acorn. And in principle it is even possible to
+combine such plugins, so that if you have, for example, a plugin for
+parsing types and a plugin for parsing JSX-style XML literals, you
+could load them both and parse code with both JSX tags and types.
+
+A plugin should register itself by adding a property to
+`acorn.plugins`, which holds a function. Calling `acorn.parse`, a
+`plugins` option can be passed, holding an object mapping plugin names
+to configuration values (or just `true` for plugins that don't take
+options). After the parser object has been created, the initialization
+functions for the chosen plugins are called with `(parser,
+configValue)` arguments. They are expected to use the `parser.extend`
+method to extend parser methods. For example, the `readToken` method
+could be extended like this:
+
+```javascript
+parser.extend("readToken", function(nextMethod) {
+  return function(code) {
+    console.log("Reading a token!")
+    return nextMethod.call(this, code)
+  }
+})
+```
+
+The `nextMethod` argument passed to `extend`'s second argument is the
+previous value of this method, and should usually be called through to
+whenever the extended method does not handle the call itself.
+
+There is a proof-of-concept JSX plugin in the [`jsx`
+branch](https://github.com/marijnh/acorn/tree/jsx) branch of the
+Github repository.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/acorn b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/acorn
new file mode 100644
index 0000000..41fd156
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/acorn
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+
+var path = require("path");
+var fs = require("fs");
+var acorn = require("../dist/acorn.js");
+
+var infile, parsed, tokens, options = {}, silent = false, compact = false, tokenize = false;
+
+function help(status) {
+  var print = (status == 0) ? console.log : console.error;
+  print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6]");
+  print("        [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--module] [--help] [--] infile");
+  process.exit(status);
+}
+
+for (var i = 2; i < process.argv.length; ++i) {
+  var arg = process.argv[i];
+  if (arg[0] != "-" && !infile) infile = arg;
+  else if (arg == "--" && !infile && i + 2 == process.argv.length) infile = process.argv[++i];
+  else if (arg == "--ecma3") options.ecmaVersion = 3;
+  else if (arg == "--ecma5") options.ecmaVersion = 5;
+  else if (arg == "--ecma6") options.ecmaVersion = 6;
+  else if (arg == "--ecma7") options.ecmaVersion = 7;
+  else if (arg == "--locations") options.locations = true;
+  else if (arg == "--allow-hash-bang") options.allowHashBang = true;
+  else if (arg == "--silent") silent = true;
+  else if (arg == "--compact") compact = true;
+  else if (arg == "--help") help(0);
+  else if (arg == "--tokenize") tokenize = true;
+  else if (arg == "--module") options.sourceType = 'module';
+  else help(1);
+}
+
+try {
+  var code = fs.readFileSync(infile, "utf8");
+
+  if (!tokenize)
+    parsed = acorn.parse(code, options);
+  else {
+    var get = acorn.tokenize(code, options);
+    tokens = [];
+    while (true) {
+      var token = get();
+      tokens.push(token);
+      if (token.type.type == "eof")
+        break;
+    }
+  }
+} catch(e) {
+  console.log(e.message);
+  process.exit(1);
+}
+
+if (!silent)
+  console.log(JSON.stringify(tokenize ? tokens : parsed, null, compact ? null : 2));
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/build-acorn.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/build-acorn.js
new file mode 100644
index 0000000..9840fe1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/build-acorn.js
@@ -0,0 +1,67 @@
+var fs = require("fs"), path = require("path")
+var stream = require("stream")
+
+var browserify = require("browserify")
+var babelify = require("babelify").configure({loose: "all"})
+
+process.chdir(path.resolve(__dirname, ".."))
+
+browserify({standalone: "acorn"})
+  .plugin(require('browserify-derequire'))
+  .transform(babelify)
+  .require("./src/index.js", {entry: true})
+  .bundle()
+  .on("error", function (err) { console.log("Error: " + err.message) })
+  .pipe(fs.createWriteStream("dist/acorn.js"))
+
+var ACORN_PLACEHOLDER = "this_function_call_should_be_replaced_with_a_call_to_load_acorn()";
+function acornShimPrepare(file) {
+  var tr = new stream.Transform
+  if (file == path.resolve(__dirname, "../src/index.js")) {
+    var sent = false
+    tr._transform = function(chunk, _, callback) {
+      if (!sent) {
+        sent = true
+        callback(null, ACORN_PLACEHOLDER);
+      } else {
+        callback()
+      }
+    }
+  } else {
+    tr._transform = function(chunk, _, callback) { callback(null, chunk) }
+  }
+  return tr
+}
+function acornShimComplete() {
+  var tr = new stream.Transform
+  var buffer = "";
+  tr._transform = function(chunk, _, callback) {
+    buffer += chunk.toString("utf8");
+    callback();
+  };
+  tr._flush = function (callback) {
+    tr.push(buffer.replace(ACORN_PLACEHOLDER, "module.exports = typeof acorn != 'undefined' ? acorn : require(\"./acorn\")"));
+    callback(null);
+  };
+  return tr;
+}
+
+browserify({standalone: "acorn.loose"})
+  .plugin(require('browserify-derequire'))
+  .transform(acornShimPrepare)
+  .transform(babelify)
+  .require("./src/loose/index.js", {entry: true})
+  .bundle()
+  .on("error", function (err) { console.log("Error: " + err.message) })
+  .pipe(acornShimComplete())
+  .pipe(fs.createWriteStream("dist/acorn_loose.js"))
+
+browserify({standalone: "acorn.walk"})
+  .plugin(require('browserify-derequire'))
+  .transform(acornShimPrepare)
+  .transform(babelify)
+  .require("./src/walk/index.js", {entry: true})
+  .bundle()
+  .on("error", function (err) { console.log("Error: " + err.message) })
+  .pipe(acornShimComplete())
+  .pipe(fs.createWriteStream("dist/walk.js"))
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/generate-identifier-regex.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/generate-identifier-regex.js
new file mode 100644
index 0000000..0d7c50f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/generate-identifier-regex.js
@@ -0,0 +1,47 @@
+// Note: run `npm install unicode-7.0.0` first.
+
+// Which Unicode version should be used?
+var version = '7.0.0';
+
+var start = require('unicode-' + version + '/properties/ID_Start/code-points')
+    .filter(function(ch) { return ch > 127; });
+var cont = [0x200c, 0x200d].concat(require('unicode-' + version + '/properties/ID_Continue/code-points')
+    .filter(function(ch) { return ch > 127 && start.indexOf(ch) == -1; }));
+
+function pad(str, width) {
+  while (str.length < width) str = "0" + str;
+  return str;
+}
+
+function esc(code) {
+  var hex = code.toString(16);
+  if (hex.length <= 2) return "\\x" + pad(hex, 2);
+  else return "\\u" + pad(hex, 4);
+}
+
+function generate(chars) {
+  var astral = [], re = "";
+  for (var i = 0, at = 0x10000; i < chars.length; i++) {
+    var from = chars[i], to = from;
+    while (i < chars.length - 1 && chars[i + 1] == to + 1) {
+      i++;
+      to++;
+    }
+    if (to <= 0xffff) {
+      if (from == to) re += esc(from);
+      else if (from + 1 == to) re += esc(from) + esc(to);
+      else re += esc(from) + "-" + esc(to);
+    } else {
+      astral.push(from - at, to - from);
+      at = to;
+    }
+  }
+  return {nonASCII: re, astral: astral};
+}
+
+var startData = generate(start), contData = generate(cont);
+
+console.log("  var nonASCIIidentifierStartChars = \"" + startData.nonASCII + "\";");
+console.log("  var nonASCIIidentifierChars = \"" + contData.nonASCII + "\";");
+console.log("  var astralIdentifierStartCodes = " + JSON.stringify(startData.astral) + ";");
+console.log("  var astralIdentifierCodes = " + JSON.stringify(contData.astral) + ";");
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/update_authors.sh b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/update_authors.sh
new file mode 100644
index 0000000..466c8db
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/update_authors.sh
@@ -0,0 +1,6 @@
+# Combine existing list of authors with everyone known in git, sort, add header.
+tail --lines=+3 AUTHORS > AUTHORS.tmp
+git log --format='%aN' | grep -v abraidwood >> AUTHORS.tmp
+echo -e "List of Acorn contributors. Updated before every release.\n" > AUTHORS
+sort -u AUTHORS.tmp >> AUTHORS
+rm -f AUTHORS.tmp
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/without_eval b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/without_eval
new file mode 100644
index 0000000..4331e70
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/bin/without_eval
@@ -0,0 +1,48 @@
+#!/usr/bin/env node
+
+var fs = require("fs")
+
+var acornSrc = fs.readFileSync(require.resolve("../dist/acorn"), "utf8")
+var acorn = require("../dist/acorn"), walk = require("../dist/walk")
+
+var ast = acorn.parse(acornSrc)
+var touchups = [], uses = []
+
+var makePred
+
+walk.simple(ast, {
+  FunctionDeclaration: function(node) {
+    if (node.id.name == "makePredicate") {
+      makePred = node
+      touchups.push({text: "// Removed to create an eval-free library", from: node.start, to: node.end})
+    }
+  },
+  ObjectExpression: function(node) {
+    node.properties.forEach(function(prop) {
+      if (prop.value.type == "CallExpression" &&
+          prop.value.callee.name == "makePredicate")
+        uses.push(prop.value)
+    })
+  }
+})
+
+var results = []
+var dryRun = acornSrc.slice(0, makePred.end) + "; makePredicate = (function(mp) {" +
+    "return function(words) { var r = mp(words); predicates.push(r); return r }})(makePredicate);" +
+    acornSrc.slice(makePred.end)
+;(new Function("predicates", dryRun))(results)
+
+uses.forEach(function (node, i) {
+  touchups.push({text: results[i].toString(), from: node.start, to: node.end})
+})
+
+var result = "", pos = 0
+touchups.sort(function(a, b) { return a.from - b.from })
+touchups.forEach(function(touchup) {
+  result += acornSrc.slice(pos, touchup.from)
+  result += touchup.text
+  pos = touchup.to
+})
+result += acornSrc.slice(pos)
+
+process.stdout.write(result)
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/.keep b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn.js
new file mode 100644
index 0000000..5e11d31
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn.js
@@ -0,0 +1,3312 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.acorn = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+// A recursive descent parser operates by defining functions for all
+// syntactic elements, and recursively calling those, each function
+// advancing the input stream and returning an AST node. Precedence
+// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
+// instead of `(!x)[1]` is handled by the fact that the parser
+// function that parses unary prefix operators is called first, and
+// in turn calls the function that parses `[]` subscripts — that
+// way, it'll receive the node for `x[1]` already parsed, and wraps
+// *that* in the unary operator node.
+//
+// Acorn uses an [operator precedence parser][opp] to handle binary
+// operator precedence, because it is much more compact than using
+// the technique outlined above, which uses different, nesting
+// functions to specify precedence, for all of the ten binary
+// precedence levels that JavaScript defines.
+//
+// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
+
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _identifier = _dereq_("./identifier");
+
+var _util = _dereq_("./util");
+
+var pp = _state.Parser.prototype;
+
+// Check if property name clashes with already added.
+// Object/class getters and setters are not allowed to clash —
+// either with each other or with an init property — and in
+// strict mode, init properties are also not allowed to be repeated.
+
+pp.checkPropClash = function (prop, propHash) {
+  if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return;
+  var key = prop.key,
+      name = undefined;
+  switch (key.type) {
+    case "Identifier":
+      name = key.name;break;
+    case "Literal":
+      name = String(key.value);break;
+    default:
+      return;
+  }
+  var kind = prop.kind;
+  if (this.options.ecmaVersion >= 6) {
+    if (name === "__proto__" && kind === "init") {
+      if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
+      propHash.proto = true;
+    }
+    return;
+  }
+  var other = undefined;
+  if (_util.has(propHash, name)) {
+    other = propHash[name];
+    var isGetSet = kind !== "init";
+    if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) this.raise(key.start, "Redefinition of property");
+  } else {
+    other = propHash[name] = {
+      init: false,
+      get: false,
+      set: false
+    };
+  }
+  other[kind] = true;
+};
+
+// ### Expression parsing
+
+// These nest, from the most general expression type at the top to
+// 'atomic', nondivisible expression types at the bottom. Most of
+// the functions will simply let the function(s) below them parse,
+// and, *if* the syntactic construct they handle is present, wrap
+// the AST node that the inner parser gave them in another node.
+
+// Parse a full expression. The optional arguments are used to
+// forbid the `in` operator (in for loops initalization expressions)
+// and provide reference for storing '=' operator inside shorthand
+// property assignment in contexts where both object expression
+// and object pattern might appear (so it's possible to raise
+// delayed syntax error at correct position).
+
+pp.parseExpression = function (noIn, refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos);
+  if (this.type === _tokentype.types.comma) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.expressions = [expr];
+    while (this.eat(_tokentype.types.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos));
+    return this.finishNode(node, "SequenceExpression");
+  }
+  return expr;
+};
+
+// Parse an assignment expression. This includes applications of
+// operators like `+=`.
+
+pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) {
+  if (this.type == _tokentype.types._yield && this.inGenerator) return this.parseYield();
+
+  var failOnShorthandAssign = undefined;
+  if (!refShorthandDefaultPos) {
+    refShorthandDefaultPos = { start: 0 };
+    failOnShorthandAssign = true;
+  } else {
+    failOnShorthandAssign = false;
+  }
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  if (this.type == _tokentype.types.parenL || this.type == _tokentype.types.name) this.potentialArrowAt = this.start;
+  var left = this.parseMaybeConditional(noIn, refShorthandDefaultPos);
+  if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
+  if (this.type.isAssign) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.operator = this.value;
+    node.left = this.type === _tokentype.types.eq ? this.toAssignable(left) : left;
+    refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly
+    this.checkLVal(left);
+    this.next();
+    node.right = this.parseMaybeAssign(noIn);
+    return this.finishNode(node, "AssignmentExpression");
+  } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
+    this.unexpected(refShorthandDefaultPos.start);
+  }
+  return left;
+};
+
+// Parse a ternary conditional (`?:`) operator.
+
+pp.parseMaybeConditional = function (noIn, refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseExprOps(noIn, refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  if (this.eat(_tokentype.types.question)) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.test = expr;
+    node.consequent = this.parseMaybeAssign();
+    this.expect(_tokentype.types.colon);
+    node.alternate = this.parseMaybeAssign(noIn);
+    return this.finishNode(node, "ConditionalExpression");
+  }
+  return expr;
+};
+
+// Start the precedence parser.
+
+pp.parseExprOps = function (noIn, refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseMaybeUnary(refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  return this.parseExprOp(expr, startPos, startLoc, -1, noIn);
+};
+
+// Parse binary operators with the operator precedence parsing
+// algorithm. `left` is the left-hand side of the operator.
+// `minPrec` provides context that allows the function to stop and
+// defer further parser to one of its callers when it encounters an
+// operator that has a lower precedence than the set it is parsing.
+
+pp.parseExprOp = function (left, leftStartPos, leftStartLoc, minPrec, noIn) {
+  var prec = this.type.binop;
+  if (prec != null && (!noIn || this.type !== _tokentype.types._in)) {
+    if (prec > minPrec) {
+      var node = this.startNodeAt(leftStartPos, leftStartLoc);
+      node.left = left;
+      node.operator = this.value;
+      var op = this.type;
+      this.next();
+      var startPos = this.start,
+          startLoc = this.startLoc;
+      node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn);
+      this.finishNode(node, op === _tokentype.types.logicalOR || op === _tokentype.types.logicalAND ? "LogicalExpression" : "BinaryExpression");
+      return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
+    }
+  }
+  return left;
+};
+
+// Parse unary operators, both prefix and postfix.
+
+pp.parseMaybeUnary = function (refShorthandDefaultPos) {
+  if (this.type.prefix) {
+    var node = this.startNode(),
+        update = this.type === _tokentype.types.incDec;
+    node.operator = this.value;
+    node.prefix = true;
+    this.next();
+    node.argument = this.parseMaybeUnary();
+    if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
+    if (update) this.checkLVal(node.argument);else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") this.raise(node.start, "Deleting local variable in strict mode");
+    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+  }
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseExprSubscripts(refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  while (this.type.postfix && !this.canInsertSemicolon()) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.operator = this.value;
+    node.prefix = false;
+    node.argument = expr;
+    this.checkLVal(expr);
+    this.next();
+    expr = this.finishNode(node, "UpdateExpression");
+  }
+  return expr;
+};
+
+// Parse call, dot, and `[]`-subscript expressions.
+
+pp.parseExprSubscripts = function (refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseExprAtom(refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  return this.parseSubscripts(expr, startPos, startLoc);
+};
+
+pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
+  for (;;) {
+    if (this.eat(_tokentype.types.dot)) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.object = base;
+      node.property = this.parseIdent(true);
+      node.computed = false;
+      base = this.finishNode(node, "MemberExpression");
+    } else if (this.eat(_tokentype.types.bracketL)) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.object = base;
+      node.property = this.parseExpression();
+      node.computed = true;
+      this.expect(_tokentype.types.bracketR);
+      base = this.finishNode(node, "MemberExpression");
+    } else if (!noCalls && this.eat(_tokentype.types.parenL)) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.callee = base;
+      node.arguments = this.parseExprList(_tokentype.types.parenR, false);
+      base = this.finishNode(node, "CallExpression");
+    } else if (this.type === _tokentype.types.backQuote) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.tag = base;
+      node.quasi = this.parseTemplate();
+      base = this.finishNode(node, "TaggedTemplateExpression");
+    } else {
+      return base;
+    }
+  }
+};
+
+// Parse an atomic expression — either a single token that is an
+// expression, an expression started by a keyword like `function` or
+// `new`, or an expression wrapped in punctuation like `()`, `[]`,
+// or `{}`.
+
+pp.parseExprAtom = function (refShorthandDefaultPos) {
+  var node = undefined,
+      canBeArrow = this.potentialArrowAt == this.start;
+  switch (this.type) {
+    case _tokentype.types._super:
+      if (!this.inFunction) this.raise(this.start, "'super' outside of function or class");
+    case _tokentype.types._this:
+      var type = this.type === _tokentype.types._this ? "ThisExpression" : "Super";
+      node = this.startNode();
+      this.next();
+      return this.finishNode(node, type);
+
+    case _tokentype.types._yield:
+      if (this.inGenerator) this.unexpected();
+
+    case _tokentype.types.name:
+      var startPos = this.start,
+          startLoc = this.startLoc;
+      var id = this.parseIdent(this.type !== _tokentype.types.name);
+      if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id]);
+      return id;
+
+    case _tokentype.types.regexp:
+      var value = this.value;
+      node = this.parseLiteral(value.value);
+      node.regex = { pattern: value.pattern, flags: value.flags };
+      return node;
+
+    case _tokentype.types.num:case _tokentype.types.string:
+      return this.parseLiteral(this.value);
+
+    case _tokentype.types._null:case _tokentype.types._true:case _tokentype.types._false:
+      node = this.startNode();
+      node.value = this.type === _tokentype.types._null ? null : this.type === _tokentype.types._true;
+      node.raw = this.type.keyword;
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case _tokentype.types.parenL:
+      return this.parseParenAndDistinguishExpression(canBeArrow);
+
+    case _tokentype.types.bracketL:
+      node = this.startNode();
+      this.next();
+      // check whether this is array comprehension or regular array
+      if (this.options.ecmaVersion >= 7 && this.type === _tokentype.types._for) {
+        return this.parseComprehension(node, false);
+      }
+      node.elements = this.parseExprList(_tokentype.types.bracketR, true, true, refShorthandDefaultPos);
+      return this.finishNode(node, "ArrayExpression");
+
+    case _tokentype.types.braceL:
+      return this.parseObj(false, refShorthandDefaultPos);
+
+    case _tokentype.types._function:
+      node = this.startNode();
+      this.next();
+      return this.parseFunction(node, false);
+
+    case _tokentype.types._class:
+      return this.parseClass(this.startNode(), false);
+
+    case _tokentype.types._new:
+      return this.parseNew();
+
+    case _tokentype.types.backQuote:
+      return this.parseTemplate();
+
+    default:
+      this.unexpected();
+  }
+};
+
+pp.parseLiteral = function (value) {
+  var node = this.startNode();
+  node.value = value;
+  node.raw = this.input.slice(this.start, this.end);
+  this.next();
+  return this.finishNode(node, "Literal");
+};
+
+pp.parseParenExpression = function () {
+  this.expect(_tokentype.types.parenL);
+  var val = this.parseExpression();
+  this.expect(_tokentype.types.parenR);
+  return val;
+};
+
+pp.parseParenAndDistinguishExpression = function (canBeArrow) {
+  var startPos = this.start,
+      startLoc = this.startLoc,
+      val = undefined;
+  if (this.options.ecmaVersion >= 6) {
+    this.next();
+
+    if (this.options.ecmaVersion >= 7 && this.type === _tokentype.types._for) {
+      return this.parseComprehension(this.startNodeAt(startPos, startLoc), true);
+    }
+
+    var innerStartPos = this.start,
+        innerStartLoc = this.startLoc;
+    var exprList = [],
+        first = true;
+    var refShorthandDefaultPos = { start: 0 },
+        spreadStart = undefined,
+        innerParenStart = undefined;
+    while (this.type !== _tokentype.types.parenR) {
+      first ? first = false : this.expect(_tokentype.types.comma);
+      if (this.type === _tokentype.types.ellipsis) {
+        spreadStart = this.start;
+        exprList.push(this.parseParenItem(this.parseRest()));
+        break;
+      } else {
+        if (this.type === _tokentype.types.parenL && !innerParenStart) {
+          innerParenStart = this.start;
+        }
+        exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem));
+      }
+    }
+    var innerEndPos = this.start,
+        innerEndLoc = this.startLoc;
+    this.expect(_tokentype.types.parenR);
+
+    if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) {
+      if (innerParenStart) this.unexpected(innerParenStart);
+      return this.parseParenArrowList(startPos, startLoc, exprList);
+    }
+
+    if (!exprList.length) this.unexpected(this.lastTokStart);
+    if (spreadStart) this.unexpected(spreadStart);
+    if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
+
+    if (exprList.length > 1) {
+      val = this.startNodeAt(innerStartPos, innerStartLoc);
+      val.expressions = exprList;
+      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
+    } else {
+      val = exprList[0];
+    }
+  } else {
+    val = this.parseParenExpression();
+  }
+
+  if (this.options.preserveParens) {
+    var par = this.startNodeAt(startPos, startLoc);
+    par.expression = val;
+    return this.finishNode(par, "ParenthesizedExpression");
+  } else {
+    return val;
+  }
+};
+
+pp.parseParenItem = function (item) {
+  return item;
+};
+
+pp.parseParenArrowList = function (startPos, startLoc, exprList) {
+  return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList);
+};
+
+// New's precedence is slightly tricky. It must allow its argument
+// to be a `[]` or dot subscript expression, but not a call — at
+// least, not without wrapping it in parentheses. Thus, it uses the
+
+var empty = [];
+
+pp.parseNew = function () {
+  var node = this.startNode();
+  var meta = this.parseIdent(true);
+  if (this.options.ecmaVersion >= 6 && this.eat(_tokentype.types.dot)) {
+    node.meta = meta;
+    node.property = this.parseIdent(true);
+    if (node.property.name !== "target") this.raise(node.property.start, "The only valid meta property for new is new.target");
+    return this.finishNode(node, "MetaProperty");
+  }
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
+  if (this.eat(_tokentype.types.parenL)) node.arguments = this.parseExprList(_tokentype.types.parenR, false);else node.arguments = empty;
+  return this.finishNode(node, "NewExpression");
+};
+
+// Parse template expression.
+
+pp.parseTemplateElement = function () {
+  var elem = this.startNode();
+  elem.value = {
+    raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
+    cooked: this.value
+  };
+  this.next();
+  elem.tail = this.type === _tokentype.types.backQuote;
+  return this.finishNode(elem, "TemplateElement");
+};
+
+pp.parseTemplate = function () {
+  var node = this.startNode();
+  this.next();
+  node.expressions = [];
+  var curElt = this.parseTemplateElement();
+  node.quasis = [curElt];
+  while (!curElt.tail) {
+    this.expect(_tokentype.types.dollarBraceL);
+    node.expressions.push(this.parseExpression());
+    this.expect(_tokentype.types.braceR);
+    node.quasis.push(curElt = this.parseTemplateElement());
+  }
+  this.next();
+  return this.finishNode(node, "TemplateLiteral");
+};
+
+// Parse an object literal or binding pattern.
+
+pp.parseObj = function (isPattern, refShorthandDefaultPos) {
+  var node = this.startNode(),
+      first = true,
+      propHash = {};
+  node.properties = [];
+  this.next();
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
+    } else first = false;
+
+    var prop = this.startNode(),
+        isGenerator = undefined,
+        startPos = undefined,
+        startLoc = undefined;
+    if (this.options.ecmaVersion >= 6) {
+      prop.method = false;
+      prop.shorthand = false;
+      if (isPattern || refShorthandDefaultPos) {
+        startPos = this.start;
+        startLoc = this.startLoc;
+      }
+      if (!isPattern) isGenerator = this.eat(_tokentype.types.star);
+    }
+    this.parsePropertyName(prop);
+    this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos);
+    this.checkPropClash(prop, propHash);
+    node.properties.push(this.finishNode(prop, "Property"));
+  }
+  return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
+};
+
+pp.parsePropertyValue = function (prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {
+  if (this.eat(_tokentype.types.colon)) {
+    prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
+    prop.kind = "init";
+  } else if (this.options.ecmaVersion >= 6 && this.type === _tokentype.types.parenL) {
+    if (isPattern) this.unexpected();
+    prop.kind = "init";
+    prop.method = true;
+    prop.value = this.parseMethod(isGenerator);
+  } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (this.type != _tokentype.types.comma && this.type != _tokentype.types.braceR)) {
+    if (isGenerator || isPattern) this.unexpected();
+    prop.kind = prop.key.name;
+    this.parsePropertyName(prop);
+    prop.value = this.parseMethod(false);
+    var paramCount = prop.kind === "get" ? 0 : 1;
+    if (prop.value.params.length !== paramCount) {
+      var start = prop.value.start;
+      if (prop.kind === "get") this.raise(start, "getter should have no params");else this.raise(start, "setter should have exactly one param");
+    }
+  } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
+    prop.kind = "init";
+    if (isPattern) {
+      if (this.isKeyword(prop.key.name) || this.strict && (_identifier.reservedWords.strictBind(prop.key.name) || _identifier.reservedWords.strict(prop.key.name)) || !this.options.allowReserved && this.isReservedWord(prop.key.name)) this.raise(prop.key.start, "Binding " + prop.key.name);
+      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
+    } else if (this.type === _tokentype.types.eq && refShorthandDefaultPos) {
+      if (!refShorthandDefaultPos.start) refShorthandDefaultPos.start = this.start;
+      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
+    } else {
+      prop.value = prop.key;
+    }
+    prop.shorthand = true;
+  } else this.unexpected();
+};
+
+pp.parsePropertyName = function (prop) {
+  if (this.options.ecmaVersion >= 6) {
+    if (this.eat(_tokentype.types.bracketL)) {
+      prop.computed = true;
+      prop.key = this.parseMaybeAssign();
+      this.expect(_tokentype.types.bracketR);
+      return prop.key;
+    } else {
+      prop.computed = false;
+    }
+  }
+  return prop.key = this.type === _tokentype.types.num || this.type === _tokentype.types.string ? this.parseExprAtom() : this.parseIdent(true);
+};
+
+// Initialize empty function node.
+
+pp.initFunction = function (node) {
+  node.id = null;
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = false;
+    node.expression = false;
+  }
+};
+
+// Parse object or class method.
+
+pp.parseMethod = function (isGenerator) {
+  var node = this.startNode();
+  this.initFunction(node);
+  this.expect(_tokentype.types.parenL);
+  node.params = this.parseBindingList(_tokentype.types.parenR, false, false);
+  var allowExpressionBody = undefined;
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = isGenerator;
+  }
+  this.parseFunctionBody(node, false);
+  return this.finishNode(node, "FunctionExpression");
+};
+
+// Parse arrow function expression with given parameters.
+
+pp.parseArrowExpression = function (node, params) {
+  this.initFunction(node);
+  node.params = this.toAssignableList(params, true);
+  this.parseFunctionBody(node, true);
+  return this.finishNode(node, "ArrowFunctionExpression");
+};
+
+// Parse function body and check parameters.
+
+pp.parseFunctionBody = function (node, allowExpression) {
+  var isExpression = allowExpression && this.type !== _tokentype.types.braceL;
+
+  if (isExpression) {
+    node.body = this.parseMaybeAssign();
+    node.expression = true;
+  } else {
+    // Start a new scope with regard to labels and the `inFunction`
+    // flag (restore them to their old value afterwards).
+    var oldInFunc = this.inFunction,
+        oldInGen = this.inGenerator,
+        oldLabels = this.labels;
+    this.inFunction = true;this.inGenerator = node.generator;this.labels = [];
+    node.body = this.parseBlock(true);
+    node.expression = false;
+    this.inFunction = oldInFunc;this.inGenerator = oldInGen;this.labels = oldLabels;
+  }
+
+  // If this is a strict mode function, verify that argument names
+  // are not repeated, and it does not try to bind the words `eval`
+  // or `arguments`.
+  if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {
+    var nameHash = {},
+        oldStrict = this.strict;
+    this.strict = true;
+    if (node.id) this.checkLVal(node.id, true);
+    for (var i = 0; i < node.params.length; i++) {
+      this.checkLVal(node.params[i], true, nameHash);
+    }this.strict = oldStrict;
+  }
+};
+
+// Parses a comma-separated list of expressions, and returns them as
+// an array. `close` is the token type that ends the list, and
+// `allowEmpty` can be turned on to allow subsequent commas with
+// nothing in between them to be parsed as `null` (which is needed
+// for array literals).
+
+pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
+  var elts = [],
+      first = true;
+  while (!this.eat(close)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (allowTrailingComma && this.afterTrailingComma(close)) break;
+    } else first = false;
+
+    var elt = undefined;
+    if (allowEmpty && this.type === _tokentype.types.comma) elt = null;else if (this.type === _tokentype.types.ellipsis) elt = this.parseSpread(refShorthandDefaultPos);else elt = this.parseMaybeAssign(false, refShorthandDefaultPos);
+    elts.push(elt);
+  }
+  return elts;
+};
+
+// Parse the next token as an identifier. If `liberal` is true (used
+// when parsing properties), it will also convert keywords into
+// identifiers.
+
+pp.parseIdent = function (liberal) {
+  var node = this.startNode();
+  if (liberal && this.options.allowReserved == "never") liberal = false;
+  if (this.type === _tokentype.types.name) {
+    if (!liberal && (!this.options.allowReserved && this.isReservedWord(this.value) || this.strict && _identifier.reservedWords.strict(this.value) && (this.options.ecmaVersion >= 6 || this.input.slice(this.start, this.end).indexOf("\\") == -1))) this.raise(this.start, "The keyword '" + this.value + "' is reserved");
+    node.name = this.value;
+  } else if (liberal && this.type.keyword) {
+    node.name = this.type.keyword;
+  } else {
+    this.unexpected();
+  }
+  this.next();
+  return this.finishNode(node, "Identifier");
+};
+
+// Parses yield expression inside generator.
+
+pp.parseYield = function () {
+  var node = this.startNode();
+  this.next();
+  if (this.type == _tokentype.types.semi || this.canInsertSemicolon() || this.type != _tokentype.types.star && !this.type.startsExpr) {
+    node.delegate = false;
+    node.argument = null;
+  } else {
+    node.delegate = this.eat(_tokentype.types.star);
+    node.argument = this.parseMaybeAssign();
+  }
+  return this.finishNode(node, "YieldExpression");
+};
+
+// Parses array and generator comprehensions.
+
+pp.parseComprehension = function (node, isGenerator) {
+  node.blocks = [];
+  while (this.type === _tokentype.types._for) {
+    var block = this.startNode();
+    this.next();
+    this.expect(_tokentype.types.parenL);
+    block.left = this.parseBindingAtom();
+    this.checkLVal(block.left, true);
+    this.expectContextual("of");
+    block.right = this.parseExpression();
+    this.expect(_tokentype.types.parenR);
+    node.blocks.push(this.finishNode(block, "ComprehensionBlock"));
+  }
+  node.filter = this.eat(_tokentype.types._if) ? this.parseParenExpression() : null;
+  node.body = this.parseExpression();
+  this.expect(isGenerator ? _tokentype.types.parenR : _tokentype.types.bracketR);
+  node.generator = isGenerator;
+  return this.finishNode(node, "ComprehensionExpression");
+};
+
+},{"./identifier":2,"./state":10,"./tokentype":14,"./util":15}],2:[function(_dereq_,module,exports){
+// This is a trick taken from Esprima. It turns out that, on
+// non-Chrome browsers, to check whether a string is in a set, a
+// predicate containing a big ugly `switch` statement is faster than
+// a regular expression, and on Chrome the two are about on par.
+// This function uses `eval` (non-lexical) to produce such a
+// predicate from a space-separated string of words.
+//
+// It starts by sorting the words by length.
+
+"use strict";
+
+exports.__esModule = true;
+exports.isIdentifierStart = isIdentifierStart;
+exports.isIdentifierChar = isIdentifierChar;
+function makePredicate(words) {
+  words = words.split(" ");
+  var f = "",
+      cats = [];
+  out: for (var i = 0; i < words.length; ++i) {
+    for (var j = 0; j < cats.length; ++j) {
+      if (cats[j][0].length == words[i].length) {
+        cats[j].push(words[i]);
+        continue out;
+      }
+    }cats.push([words[i]]);
+  }
+  function compareTo(arr) {
+    if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
+    f += "switch(str){";
+    for (var i = 0; i < arr.length; ++i) {
+      f += "case " + JSON.stringify(arr[i]) + ":";
+    }f += "return true}return false;";
+  }
+
+  // When there are more than three length categories, an outer
+  // switch first dispatches on the lengths, to save on comparisons.
+
+  if (cats.length > 3) {
+    cats.sort(function (a, b) {
+      return b.length - a.length;
+    });
+    f += "switch(str.length){";
+    for (var i = 0; i < cats.length; ++i) {
+      var cat = cats[i];
+      f += "case " + cat[0].length + ":";
+      compareTo(cat);
+    }
+    f += "}"
+
+    // Otherwise, simply generate a flat `switch` statement.
+
+    ;
+  } else {
+    compareTo(words);
+  }
+  return new Function("str", f);
+}
+
+// Reserved word lists for various dialects of the language
+
+var reservedWords = {
+  3: makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),
+  5: makePredicate("class enum extends super const export import"),
+  6: makePredicate("enum await"),
+  strict: makePredicate("implements interface let package private protected public static yield"),
+  strictBind: makePredicate("eval arguments")
+};
+
+exports.reservedWords = reservedWords;
+// And the keywords
+
+var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";
+
+var keywords = {
+  5: makePredicate(ecma5AndLessKeywords),
+  6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super")
+};
+
+exports.keywords = keywords;
+// ## Character categories
+
+// Big ugly regular expressions that match characters in the
+// whitespace, identifier, and identifier-start categories. These
+// are only applied when a character is found to actually have a
+// code point above 128.
+// Generated by `tools/generate-identifier-regex.js`.
+
+var nonASCIIidentifierStartChars = "ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠ-ࢲऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖ゛-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞭꞰꞱꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭟꭤꭥꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ﬀ-ﬆﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼＡ-Ｚａ-ｚｦ-ﾾￂ-ￇￊ-ￏￒ-ￗￚ-ￜ";
+var nonASCIIidentifierChars = "‌‍·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-٩ٰۖ-ۜ۟-۪ۤۧۨ-ۭ۰-۹ܑܰ-݊ަ-ް߀-߉߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣤ-ःऺ-़ा-ॏ॑-ॗॢॣ०-९ঁ-ঃ়া-ৄেৈো-্ৗৢৣ০-৯ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣ૦-૯ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఀ-ఃా-ౄె-ైొ-్ౕౖౢౣ౦-౯ಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣ೦-೯ഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣ൦-൯ංඃ්ා-ුූෘ-ෟ෦-෯ෲෳัิ-ฺ็-๎๐-๙ັິ-ູົຼ່-ໍ໐-໙༘༙༠-༩༹༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှ၀-၉ၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟፩-፱ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝០-៩᠋-᠍᠐-᠙ᢩᤠ-ᤫᤰ-᤻᥆-᥏ᦰ-ᧀᧈᧉ᧐-᧚ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼-᪉᪐-᪙᪰-᪽ᬀ-ᬄ᬴-᭄᭐-᭙᭫-᭳ᮀ-ᮂᮡ-ᮭ᮰-᮹᯦-᯳ᰤ-᰷᱀-᱉᱐-᱙᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷼-᷿‿⁀⁔⃐-⃥⃜⃡-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꘠-꘩꙯ꙴ-꙽ꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-꣄꣐-꣙꣠-꣱꤀-꤉ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀꧐-꧙ꧥ꧰-꧹ꨩ-ꨶꩃꩌꩍ꩐-꩙ꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭꯰-꯹ﬞ︀-️︠-︭︳︴﹍-﹏０-９＿";
+
+var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
+var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
+
+nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
+
+// These are a run-length and offset encoded representation of the
+// >0xffff code points that are a valid part of identifiers. The
+// offset starts at 0x10000, and each pair of numbers represents an
+// offset to the next range, and then a size of the range. They were
+// generated by tools/generate-identifier-regex.js
+var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 17, 26, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 99, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 98, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 955, 52, 76, 44, 33, 24, 27, 35, 42, 34, 4, 0, 13, 47, 15, 3, 22, 0, 38, 17, 2, 24, 133, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 32, 4, 287, 47, 21, 1, 2, 0, 185, 46, 82, 47, 21, 0, 60, 42, 502, 63, 32, 0, 449, 56, 1288, 920, 104, 110, 2962, 1070, 13266, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12, 0, 67, 12, 16481, 1, 3071, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 4149, 196, 1340, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42710, 42, 4148, 12, 221, 16355, 541];
+var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 16, 9, 83, 11, 168, 11, 6, 9, 8, 2, 57, 0, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 316, 19, 13, 9, 214, 6, 3, 8, 112, 16, 16, 9, 82, 12, 9, 9, 535, 9, 20855, 9, 135, 4, 60, 6, 26, 9, 1016, 45, 17, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 4305, 6, 792618, 239];
+
+// This has a complexity linear to the value of the code. The
+// assumption is that looking up astral identifier characters is
+// rare.
+function isInAstralSet(code, set) {
+  var pos = 0x10000;
+  for (var i = 0; i < set.length; i += 2) {
+    pos += set[i];
+    if (pos > code) return false;
+    pos += set[i + 1];
+    if (pos >= code) return true;
+  }
+}
+
+// Test whether a given character code starts an identifier.
+
+function isIdentifierStart(code, astral) {
+  if (code < 65) return code === 36;
+  if (code < 91) return true;
+  if (code < 97) return code === 95;
+  if (code < 123) return true;
+  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
+  if (astral === false) return false;
+  return isInAstralSet(code, astralIdentifierStartCodes);
+}
+
+// Test whether a given character is part of an identifier.
+
+function isIdentifierChar(code, astral) {
+  if (code < 48) return code === 36;
+  if (code < 58) return true;
+  if (code < 65) return false;
+  if (code < 91) return true;
+  if (code < 97) return code === 95;
+  if (code < 123) return true;
+  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
+  if (astral === false) return false;
+  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
+}
+
+},{}],3:[function(_dereq_,module,exports){
+// Acorn is a tiny, fast JavaScript parser written in JavaScript.
+//
+// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
+// various contributors and released under an MIT license.
+//
+// Git repositories for Acorn are available at
+//
+//     http://marijnhaverbeke.nl/git/acorn
+//     https://github.com/marijnh/acorn.git
+//
+// Please use the [github bug tracker][ghbt] to report issues.
+//
+// [ghbt]: https://github.com/marijnh/acorn/issues
+//
+// This file defines the main parser interface. The library also comes
+// with a [error-tolerant parser][dammit] and an
+// [abstract syntax tree walker][walk], defined in other files.
+//
+// [dammit]: acorn_loose.js
+// [walk]: util/walk.js
+
+"use strict";
+
+exports.__esModule = true;
+exports.parse = parse;
+exports.parseExpressionAt = parseExpressionAt;
+exports.tokenizer = tokenizer;
+
+var _state = _dereq_("./state");
+
+var _options = _dereq_("./options");
+
+_dereq_("./parseutil");
+
+_dereq_("./statement");
+
+_dereq_("./lval");
+
+_dereq_("./expression");
+
+_dereq_("./location");
+
+exports.Parser = _state.Parser;
+exports.plugins = _state.plugins;
+exports.defaultOptions = _options.defaultOptions;
+
+var _locutil = _dereq_("./locutil");
+
+exports.Position = _locutil.Position;
+exports.SourceLocation = _locutil.SourceLocation;
+exports.getLineInfo = _locutil.getLineInfo;
+
+var _node = _dereq_("./node");
+
+exports.Node = _node.Node;
+
+var _tokentype = _dereq_("./tokentype");
+
+exports.TokenType = _tokentype.TokenType;
+exports.tokTypes = _tokentype.types;
+
+var _tokencontext = _dereq_("./tokencontext");
+
+exports.TokContext = _tokencontext.TokContext;
+exports.tokContexts = _tokencontext.types;
+
+var _identifier = _dereq_("./identifier");
+
+exports.isIdentifierChar = _identifier.isIdentifierChar;
+exports.isIdentifierStart = _identifier.isIdentifierStart;
+
+var _tokenize = _dereq_("./tokenize");
+
+exports.Token = _tokenize.Token;
+
+var _whitespace = _dereq_("./whitespace");
+
+exports.isNewLine = _whitespace.isNewLine;
+exports.lineBreak = _whitespace.lineBreak;
+exports.lineBreakG = _whitespace.lineBreakG;
+var version = "2.4.0";
+
+exports.version = version;
+// The main exported interface (under `self.acorn` when in the
+// browser) is a `parse` function that takes a code string and
+// returns an abstract syntax tree as specified by [Mozilla parser
+// API][api].
+//
+// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+
+function parse(input, options) {
+  return new _state.Parser(options, input).parse();
+}
+
+// This function tries to parse a single expression at a given
+// offset in a string. Useful for parsing mixed-language formats
+// that embed JavaScript expressions.
+
+function parseExpressionAt(input, pos, options) {
+  var p = new _state.Parser(options, input, pos);
+  p.nextToken();
+  return p.parseExpression();
+}
+
+// Acorn is organized as a tokenizer and a recursive-descent parser.
+// The `tokenize` export provides an interface to the tokenizer.
+
+function tokenizer(input, options) {
+  return new _state.Parser(options, input);
+}
+
+},{"./expression":1,"./identifier":2,"./location":4,"./locutil":5,"./lval":6,"./node":7,"./options":8,"./parseutil":9,"./state":10,"./statement":11,"./tokencontext":12,"./tokenize":13,"./tokentype":14,"./whitespace":16}],4:[function(_dereq_,module,exports){
+"use strict";
+
+var _state = _dereq_("./state");
+
+var _locutil = _dereq_("./locutil");
+
+var pp = _state.Parser.prototype;
+
+// This function is used to raise exceptions on parse errors. It
+// takes an offset integer (into the current `input`) to indicate
+// the location of the error, attaches the position to the end
+// of the error message, and then raises a `SyntaxError` with that
+// message.
+
+pp.raise = function (pos, message) {
+  var loc = _locutil.getLineInfo(this.input, pos);
+  message += " (" + loc.line + ":" + loc.column + ")";
+  var err = new SyntaxError(message);
+  err.pos = pos;err.loc = loc;err.raisedAt = this.pos;
+  throw err;
+};
+
+pp.curPosition = function () {
+  if (this.options.locations) {
+    return new _locutil.Position(this.curLine, this.pos - this.lineStart);
+  }
+};
+
+},{"./locutil":5,"./state":10}],5:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.getLineInfo = getLineInfo;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _whitespace = _dereq_("./whitespace");
+
+// These are used when `options.locations` is on, for the
+// `startLoc` and `endLoc` properties.
+
+var Position = (function () {
+  function Position(line, col) {
+    _classCallCheck(this, Position);
+
+    this.line = line;
+    this.column = col;
+  }
+
+  Position.prototype.offset = function offset(n) {
+    return new Position(this.line, this.column + n);
+  };
+
+  return Position;
+})();
+
+exports.Position = Position;
+
+var SourceLocation = function SourceLocation(p, start, end) {
+  _classCallCheck(this, SourceLocation);
+
+  this.start = start;
+  this.end = end;
+  if (p.sourceFile !== null) this.source = p.sourceFile;
+};
+
+exports.SourceLocation = SourceLocation;
+
+// The `getLineInfo` function is mostly useful when the
+// `locations` option is off (for performance reasons) and you
+// want to find the line/column position for a given character
+// offset. `input` should be the code string that the offset refers
+// into.
+
+function getLineInfo(input, offset) {
+  for (var line = 1, cur = 0;;) {
+    _whitespace.lineBreakG.lastIndex = cur;
+    var match = _whitespace.lineBreakG.exec(input);
+    if (match && match.index < offset) {
+      ++line;
+      cur = match.index + match[0].length;
+    } else {
+      return new Position(line, offset - cur);
+    }
+  }
+}
+
+},{"./whitespace":16}],6:[function(_dereq_,module,exports){
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _identifier = _dereq_("./identifier");
+
+var _util = _dereq_("./util");
+
+var pp = _state.Parser.prototype;
+
+// Convert existing expression atom to assignable pattern
+// if possible.
+
+pp.toAssignable = function (node, isBinding) {
+  if (this.options.ecmaVersion >= 6 && node) {
+    switch (node.type) {
+      case "Identifier":
+      case "ObjectPattern":
+      case "ArrayPattern":
+      case "AssignmentPattern":
+        break;
+
+      case "ObjectExpression":
+        node.type = "ObjectPattern";
+        for (var i = 0; i < node.properties.length; i++) {
+          var prop = node.properties[i];
+          if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter");
+          this.toAssignable(prop.value, isBinding);
+        }
+        break;
+
+      case "ArrayExpression":
+        node.type = "ArrayPattern";
+        this.toAssignableList(node.elements, isBinding);
+        break;
+
+      case "AssignmentExpression":
+        if (node.operator === "=") {
+          node.type = "AssignmentPattern";
+          delete node.operator;
+        } else {
+          this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
+        }
+        break;
+
+      case "ParenthesizedExpression":
+        node.expression = this.toAssignable(node.expression, isBinding);
+        break;
+
+      case "MemberExpression":
+        if (!isBinding) break;
+
+      default:
+        this.raise(node.start, "Assigning to rvalue");
+    }
+  }
+  return node;
+};
+
+// Convert list of expression atoms to binding list.
+
+pp.toAssignableList = function (exprList, isBinding) {
+  var end = exprList.length;
+  if (end) {
+    var last = exprList[end - 1];
+    if (last && last.type == "RestElement") {
+      --end;
+    } else if (last && last.type == "SpreadElement") {
+      last.type = "RestElement";
+      var arg = last.argument;
+      this.toAssignable(arg, isBinding);
+      if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") this.unexpected(arg.start);
+      --end;
+    }
+  }
+  for (var i = 0; i < end; i++) {
+    var elt = exprList[i];
+    if (elt) this.toAssignable(elt, isBinding);
+  }
+  return exprList;
+};
+
+// Parses spread element.
+
+pp.parseSpread = function (refShorthandDefaultPos) {
+  var node = this.startNode();
+  this.next();
+  node.argument = this.parseMaybeAssign(refShorthandDefaultPos);
+  return this.finishNode(node, "SpreadElement");
+};
+
+pp.parseRest = function () {
+  var node = this.startNode();
+  this.next();
+  node.argument = this.type === _tokentype.types.name || this.type === _tokentype.types.bracketL ? this.parseBindingAtom() : this.unexpected();
+  return this.finishNode(node, "RestElement");
+};
+
+// Parses lvalue (assignable) atom.
+
+pp.parseBindingAtom = function () {
+  if (this.options.ecmaVersion < 6) return this.parseIdent();
+  switch (this.type) {
+    case _tokentype.types.name:
+      return this.parseIdent();
+
+    case _tokentype.types.bracketL:
+      var node = this.startNode();
+      this.next();
+      node.elements = this.parseBindingList(_tokentype.types.bracketR, true, true);
+      return this.finishNode(node, "ArrayPattern");
+
+    case _tokentype.types.braceL:
+      return this.parseObj(true);
+
+    default:
+      this.unexpected();
+  }
+};
+
+pp.parseBindingList = function (close, allowEmpty, allowTrailingComma) {
+  var elts = [],
+      first = true;
+  while (!this.eat(close)) {
+    if (first) first = false;else this.expect(_tokentype.types.comma);
+    if (allowEmpty && this.type === _tokentype.types.comma) {
+      elts.push(null);
+    } else if (allowTrailingComma && this.afterTrailingComma(close)) {
+      break;
+    } else if (this.type === _tokentype.types.ellipsis) {
+      var rest = this.parseRest();
+      this.parseBindingListItem(rest);
+      elts.push(rest);
+      this.expect(close);
+      break;
+    } else {
+      var elem = this.parseMaybeDefault(this.start, this.startLoc);
+      this.parseBindingListItem(elem);
+      elts.push(elem);
+    }
+  }
+  return elts;
+};
+
+pp.parseBindingListItem = function (param) {
+  return param;
+};
+
+// Parses assignment pattern around given atom if possible.
+
+pp.parseMaybeDefault = function (startPos, startLoc, left) {
+  left = left || this.parseBindingAtom();
+  if (this.options.ecmaVersion < 6 || !this.eat(_tokentype.types.eq)) return left;
+  var node = this.startNodeAt(startPos, startLoc);
+  node.left = left;
+  node.right = this.parseMaybeAssign();
+  return this.finishNode(node, "AssignmentPattern");
+};
+
+// Verify that a node is an lval — something that can be assigned
+// to.
+
+pp.checkLVal = function (expr, isBinding, checkClashes) {
+  switch (expr.type) {
+    case "Identifier":
+      if (this.strict && (_identifier.reservedWords.strictBind(expr.name) || _identifier.reservedWords.strict(expr.name))) this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode");
+      if (checkClashes) {
+        if (_util.has(checkClashes, expr.name)) this.raise(expr.start, "Argument name clash in strict mode");
+        checkClashes[expr.name] = true;
+      }
+      break;
+
+    case "MemberExpression":
+      if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
+      break;
+
+    case "ObjectPattern":
+      for (var i = 0; i < expr.properties.length; i++) {
+        this.checkLVal(expr.properties[i].value, isBinding, checkClashes);
+      }break;
+
+    case "ArrayPattern":
+      for (var i = 0; i < expr.elements.length; i++) {
+        var elem = expr.elements[i];
+        if (elem) this.checkLVal(elem, isBinding, checkClashes);
+      }
+      break;
+
+    case "AssignmentPattern":
+      this.checkLVal(expr.left, isBinding, checkClashes);
+      break;
+
+    case "RestElement":
+      this.checkLVal(expr.argument, isBinding, checkClashes);
+      break;
+
+    case "ParenthesizedExpression":
+      this.checkLVal(expr.expression, isBinding, checkClashes);
+      break;
+
+    default:
+      this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue");
+  }
+};
+
+},{"./identifier":2,"./state":10,"./tokentype":14,"./util":15}],7:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _state = _dereq_("./state");
+
+var _locutil = _dereq_("./locutil");
+
+var Node = function Node(parser, pos, loc) {
+  _classCallCheck(this, Node);
+
+  this.type = "";
+  this.start = pos;
+  this.end = 0;
+  if (parser.options.locations) this.loc = new _locutil.SourceLocation(parser, loc);
+  if (parser.options.directSourceFile) this.sourceFile = parser.options.directSourceFile;
+  if (parser.options.ranges) this.range = [pos, 0];
+};
+
+exports.Node = Node;
+
+// Start an AST node, attaching a start offset.
+
+var pp = _state.Parser.prototype;
+
+pp.startNode = function () {
+  return new Node(this, this.start, this.startLoc);
+};
+
+pp.startNodeAt = function (pos, loc) {
+  return new Node(this, pos, loc);
+};
+
+// Finish an AST node, adding `type` and `end` properties.
+
+function finishNodeAt(node, type, pos, loc) {
+  node.type = type;
+  node.end = pos;
+  if (this.options.locations) node.loc.end = loc;
+  if (this.options.ranges) node.range[1] = pos;
+  return node;
+}
+
+pp.finishNode = function (node, type) {
+  return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc);
+};
+
+// Finish node at given position
+
+pp.finishNodeAt = function (node, type, pos, loc) {
+  return finishNodeAt.call(this, node, type, pos, loc);
+};
+
+},{"./locutil":5,"./state":10}],8:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.getOptions = getOptions;
+
+var _util = _dereq_("./util");
+
+var _locutil = _dereq_("./locutil");
+
+// A second optional argument can be given to further configure
+// the parser process. These options are recognized:
+
+var defaultOptions = {
+  // `ecmaVersion` indicates the ECMAScript version to parse. Must
+  // be either 3, or 5, or 6. This influences support for strict
+  // mode, the set of reserved words, support for getters and
+  // setters and other features.
+  ecmaVersion: 5,
+  // Source type ("script" or "module") for different semantics
+  sourceType: "script",
+  // `onInsertedSemicolon` can be a callback that will be called
+  // when a semicolon is automatically inserted. It will be passed
+  // th position of the comma as an offset, and if `locations` is
+  // enabled, it is given the location as a `{line, column}` object
+  // as second argument.
+  onInsertedSemicolon: null,
+  // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
+  // trailing commas.
+  onTrailingComma: null,
+  // By default, reserved words are not enforced. Disable
+  // `allowReserved` to enforce them. When this option has the
+  // value "never", reserved words and keywords can also not be
+  // used as property names.
+  allowReserved: true,
+  // When enabled, a return at the top level is not considered an
+  // error.
+  allowReturnOutsideFunction: false,
+  // When enabled, import/export statements are not constrained to
+  // appearing at the top of the program.
+  allowImportExportEverywhere: false,
+  // When enabled, hashbang directive in the beginning of file
+  // is allowed and treated as a line comment.
+  allowHashBang: false,
+  // When `locations` is on, `loc` properties holding objects with
+  // `start` and `end` properties in `{line, column}` form (with
+  // line being 1-based and column 0-based) will be attached to the
+  // nodes.
+  locations: false,
+  // A function can be passed as `onToken` option, which will
+  // cause Acorn to call that function with object in the same
+  // format as tokenize() returns. Note that you are not
+  // allowed to call the parser from the callback—that will
+  // corrupt its internal state.
+  onToken: null,
+  // A function can be passed as `onComment` option, which will
+  // cause Acorn to call that function with `(block, text, start,
+  // end)` parameters whenever a comment is skipped. `block` is a
+  // boolean indicating whether this is a block (`/* */`) comment,
+  // `text` is the content of the comment, and `start` and `end` are
+  // character offsets that denote the start and end of the comment.
+  // When the `locations` option is on, two more parameters are
+  // passed, the full `{line, column}` locations of the start and
+  // end of the comments. Note that you are not allowed to call the
+  // parser from the callback—that will corrupt its internal state.
+  onComment: null,
+  // Nodes have their start and end characters offsets recorded in
+  // `start` and `end` properties (directly on the node, rather than
+  // the `loc` object, which holds line/column data. To also add a
+  // [semi-standardized][range] `range` property holding a `[start,
+  // end]` array with the same numbers, set the `ranges` option to
+  // `true`.
+  //
+  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
+  ranges: false,
+  // It is possible to parse multiple files into a single AST by
+  // passing the tree produced by parsing the first file as
+  // `program` option in subsequent parses. This will add the
+  // toplevel forms of the parsed file to the `Program` (top) node
+  // of an existing parse tree.
+  program: null,
+  // When `locations` is on, you can pass this to record the source
+  // file in every node's `loc` object.
+  sourceFile: null,
+  // This value, if given, is stored in every node, whether
+  // `locations` is on or off.
+  directSourceFile: null,
+  // When enabled, parenthesized expressions are represented by
+  // (non-standard) ParenthesizedExpression nodes
+  preserveParens: false,
+  plugins: {}
+};
+
+exports.defaultOptions = defaultOptions;
+// Interpret and default an options object
+
+function getOptions(opts) {
+  var options = {};
+  for (var opt in defaultOptions) {
+    options[opt] = opts && _util.has(opts, opt) ? opts[opt] : defaultOptions[opt];
+  }if (_util.isArray(options.onToken)) {
+    (function () {
+      var tokens = options.onToken;
+      options.onToken = function (token) {
+        return tokens.push(token);
+      };
+    })();
+  }
+  if (_util.isArray(options.onComment)) options.onComment = pushComment(options, options.onComment);
+
+  return options;
+}
+
+function pushComment(options, array) {
+  return function (block, text, start, end, startLoc, endLoc) {
+    var comment = {
+      type: block ? "Block" : "Line",
+      value: text,
+      start: start,
+      end: end
+    };
+    if (options.locations) comment.loc = new _locutil.SourceLocation(this, startLoc, endLoc);
+    if (options.ranges) comment.range = [start, end];
+    array.push(comment);
+  };
+}
+
+},{"./locutil":5,"./util":15}],9:[function(_dereq_,module,exports){
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _whitespace = _dereq_("./whitespace");
+
+var pp = _state.Parser.prototype;
+
+// ## Parser utilities
+
+// Test whether a statement node is the string literal `"use strict"`.
+
+pp.isUseStrict = function (stmt) {
+  return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && stmt.expression.type === "Literal" && stmt.expression.raw.slice(1, -1) === "use strict";
+};
+
+// Predicate that tests whether the next token is of the given
+// type, and if yes, consumes it as a side effect.
+
+pp.eat = function (type) {
+  if (this.type === type) {
+    this.next();
+    return true;
+  } else {
+    return false;
+  }
+};
+
+// Tests whether parsed token is a contextual keyword.
+
+pp.isContextual = function (name) {
+  return this.type === _tokentype.types.name && this.value === name;
+};
+
+// Consumes contextual keyword if possible.
+
+pp.eatContextual = function (name) {
+  return this.value === name && this.eat(_tokentype.types.name);
+};
+
+// Asserts that following token is given contextual keyword.
+
+pp.expectContextual = function (name) {
+  if (!this.eatContextual(name)) this.unexpected();
+};
+
+// Test whether a semicolon can be inserted at the current position.
+
+pp.canInsertSemicolon = function () {
+  return this.type === _tokentype.types.eof || this.type === _tokentype.types.braceR || _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
+};
+
+pp.insertSemicolon = function () {
+  if (this.canInsertSemicolon()) {
+    if (this.options.onInsertedSemicolon) this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc);
+    return true;
+  }
+};
+
+// Consume a semicolon, or, failing that, see if we are allowed to
+// pretend that there is a semicolon at this position.
+
+pp.semicolon = function () {
+  if (!this.eat(_tokentype.types.semi) && !this.insertSemicolon()) this.unexpected();
+};
+
+pp.afterTrailingComma = function (tokType) {
+  if (this.type == tokType) {
+    if (this.options.onTrailingComma) this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
+    this.next();
+    return true;
+  }
+};
+
+// Expect a token of a given type. If found, consume it, otherwise,
+// raise an unexpected token error.
+
+pp.expect = function (type) {
+  this.eat(type) || this.unexpected();
+};
+
+// Raise an unexpected token error.
+
+pp.unexpected = function (pos) {
+  this.raise(pos != null ? pos : this.start, "Unexpected token");
+};
+
+},{"./state":10,"./tokentype":14,"./whitespace":16}],10:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _identifier = _dereq_("./identifier");
+
+var _tokentype = _dereq_("./tokentype");
+
+var _whitespace = _dereq_("./whitespace");
+
+var _options = _dereq_("./options");
+
+// Registered plugins
+var plugins = {};
+
+exports.plugins = plugins;
+
+var Parser = (function () {
+  function Parser(options, input, startPos) {
+    _classCallCheck(this, Parser);
+
+    this.options = _options.getOptions(options);
+    this.sourceFile = this.options.sourceFile;
+    this.isKeyword = _identifier.keywords[this.options.ecmaVersion >= 6 ? 6 : 5];
+    this.isReservedWord = _identifier.reservedWords[this.options.ecmaVersion];
+    this.input = String(input);
+
+    // Used to signal to callers of `readWord1` whether the word
+    // contained any escape sequences. This is needed because words with
+    // escape sequences must not be interpreted as keywords.
+    this.containsEsc = false;
+
+    // Load plugins
+    this.loadPlugins(this.options.plugins);
+
+    // Set up token state
+
+    // The current position of the tokenizer in the input.
+    if (startPos) {
+      this.pos = startPos;
+      this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos));
+      this.curLine = this.input.slice(0, this.lineStart).split(_whitespace.lineBreak).length;
+    } else {
+      this.pos = this.lineStart = 0;
+      this.curLine = 1;
+    }
+
+    // Properties of the current token:
+    // Its type
+    this.type = _tokentype.types.eof;
+    // For tokens that include more information than their type, the value
+    this.value = null;
+    // Its start and end offset
+    this.start = this.end = this.pos;
+    // And, if locations are used, the {line, column} object
+    // corresponding to those offsets
+    this.startLoc = this.endLoc = this.curPosition();
+
+    // Position information for the previous token
+    this.lastTokEndLoc = this.lastTokStartLoc = null;
+    this.lastTokStart = this.lastTokEnd = this.pos;
+
+    // The context stack is used to superficially track syntactic
+    // context to predict whether a regular expression is allowed in a
+    // given position.
+    this.context = this.initialContext();
+    this.exprAllowed = true;
+
+    // Figure out if it's a module code.
+    this.strict = this.inModule = this.options.sourceType === "module";
+
+    // Used to signify the start of a potential arrow function
+    this.potentialArrowAt = -1;
+
+    // Flags to track whether we are in a function, a generator.
+    this.inFunction = this.inGenerator = false;
+    // Labels in scope.
+    this.labels = [];
+
+    // If enabled, skip leading hashbang line.
+    if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === "#!") this.skipLineComment(2);
+  }
+
+  Parser.prototype.extend = function extend(name, f) {
+    this[name] = f(this[name]);
+  };
+
+  Parser.prototype.loadPlugins = function loadPlugins(pluginConfigs) {
+    for (var _name in pluginConfigs) {
+      var plugin = plugins[_name];
+      if (!plugin) throw new Error("Plugin '" + _name + "' not found");
+      plugin(this, pluginConfigs[_name]);
+    }
+  };
+
+  Parser.prototype.parse = function parse() {
+    var node = this.options.program || this.startNode();
+    this.nextToken();
+    return this.parseTopLevel(node);
+  };
+
+  return Parser;
+})();
+
+exports.Parser = Parser;
+
+},{"./identifier":2,"./options":8,"./tokentype":14,"./whitespace":16}],11:[function(_dereq_,module,exports){
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _whitespace = _dereq_("./whitespace");
+
+var pp = _state.Parser.prototype;
+
+// ### Statement parsing
+
+// Parse a program. Initializes the parser, reads any number of
+// statements, and wraps them in a Program node.  Optionally takes a
+// `program` argument.  If present, the statements will be appended
+// to its body instead of creating a new node.
+
+pp.parseTopLevel = function (node) {
+  var first = true;
+  if (!node.body) node.body = [];
+  while (this.type !== _tokentype.types.eof) {
+    var stmt = this.parseStatement(true, true);
+    node.body.push(stmt);
+    if (first) {
+      if (this.isUseStrict(stmt)) this.setStrict(true);
+      first = false;
+    }
+  }
+  this.next();
+  if (this.options.ecmaVersion >= 6) {
+    node.sourceType = this.options.sourceType;
+  }
+  return this.finishNode(node, "Program");
+};
+
+var loopLabel = { kind: "loop" },
+    switchLabel = { kind: "switch" };
+
+// Parse a single statement.
+//
+// If expecting a statement and finding a slash operator, parse a
+// regular expression literal. This is to handle cases like
+// `if (foo) /blah/.exec(foo)`, where looking at the previous token
+// does not help.
+
+pp.parseStatement = function (declaration, topLevel) {
+  var starttype = this.type,
+      node = this.startNode();
+
+  // Most types of statements are recognized by the keyword they
+  // start with. Many are trivial to parse, some require a bit of
+  // complexity.
+
+  switch (starttype) {
+    case _tokentype.types._break:case _tokentype.types._continue:
+      return this.parseBreakContinueStatement(node, starttype.keyword);
+    case _tokentype.types._debugger:
+      return this.parseDebuggerStatement(node);
+    case _tokentype.types._do:
+      return this.parseDoStatement(node);
+    case _tokentype.types._for:
+      return this.parseForStatement(node);
+    case _tokentype.types._function:
+      if (!declaration && this.options.ecmaVersion >= 6) this.unexpected();
+      return this.parseFunctionStatement(node);
+    case _tokentype.types._class:
+      if (!declaration) this.unexpected();
+      return this.parseClass(node, true);
+    case _tokentype.types._if:
+      return this.parseIfStatement(node);
+    case _tokentype.types._return:
+      return this.parseReturnStatement(node);
+    case _tokentype.types._switch:
+      return this.parseSwitchStatement(node);
+    case _tokentype.types._throw:
+      return this.parseThrowStatement(node);
+    case _tokentype.types._try:
+      return this.parseTryStatement(node);
+    case _tokentype.types._let:case _tokentype.types._const:
+      if (!declaration) this.unexpected(); // NOTE: falls through to _var
+    case _tokentype.types._var:
+      return this.parseVarStatement(node, starttype);
+    case _tokentype.types._while:
+      return this.parseWhileStatement(node);
+    case _tokentype.types._with:
+      return this.parseWithStatement(node);
+    case _tokentype.types.braceL:
+      return this.parseBlock();
+    case _tokentype.types.semi:
+      return this.parseEmptyStatement(node);
+    case _tokentype.types._export:
+    case _tokentype.types._import:
+      if (!this.options.allowImportExportEverywhere) {
+        if (!topLevel) this.raise(this.start, "'import' and 'export' may only appear at the top level");
+        if (!this.inModule) this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'");
+      }
+      return starttype === _tokentype.types._import ? this.parseImport(node) : this.parseExport(node);
+
+    // If the statement does not start with a statement keyword or a
+    // brace, it's an ExpressionStatement or LabeledStatement. We
+    // simply start parsing an expression, and afterwards, if the
+    // next token is a colon and the expression was a simple
+    // Identifier node, we switch to interpreting it as a label.
+    default:
+      var maybeName = this.value,
+          expr = this.parseExpression();
+      if (starttype === _tokentype.types.name && expr.type === "Identifier" && this.eat(_tokentype.types.colon)) return this.parseLabeledStatement(node, maybeName, expr);else return this.parseExpressionStatement(node, expr);
+  }
+};
+
+pp.parseBreakContinueStatement = function (node, keyword) {
+  var isBreak = keyword == "break";
+  this.next();
+  if (this.eat(_tokentype.types.semi) || this.insertSemicolon()) node.label = null;else if (this.type !== _tokentype.types.name) this.unexpected();else {
+    node.label = this.parseIdent();
+    this.semicolon();
+  }
+
+  // Verify that there is an actual destination to break or
+  // continue to.
+  for (var i = 0; i < this.labels.length; ++i) {
+    var lab = this.labels[i];
+    if (node.label == null || lab.name === node.label.name) {
+      if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
+      if (node.label && isBreak) break;
+    }
+  }
+  if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword);
+  return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+};
+
+pp.parseDebuggerStatement = function (node) {
+  this.next();
+  this.semicolon();
+  return this.finishNode(node, "DebuggerStatement");
+};
+
+pp.parseDoStatement = function (node) {
+  this.next();
+  this.labels.push(loopLabel);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  this.expect(_tokentype.types._while);
+  node.test = this.parseParenExpression();
+  if (this.options.ecmaVersion >= 6) this.eat(_tokentype.types.semi);else this.semicolon();
+  return this.finishNode(node, "DoWhileStatement");
+};
+
+// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
+// loop is non-trivial. Basically, we have to parse the init `var`
+// statement or expression, disallowing the `in` operator (see
+// the second parameter to `parseExpression`), and then check
+// whether the next token is `in` or `of`. When there is no init
+// part (semicolon immediately after the opening parenthesis), it
+// is a regular `for` loop.
+
+pp.parseForStatement = function (node) {
+  this.next();
+  this.labels.push(loopLabel);
+  this.expect(_tokentype.types.parenL);
+  if (this.type === _tokentype.types.semi) return this.parseFor(node, null);
+  if (this.type === _tokentype.types._var || this.type === _tokentype.types._let || this.type === _tokentype.types._const) {
+    var _init = this.startNode(),
+        varKind = this.type;
+    this.next();
+    this.parseVar(_init, true, varKind);
+    this.finishNode(_init, "VariableDeclaration");
+    if ((this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) && _init.declarations.length === 1 && !(varKind !== _tokentype.types._var && _init.declarations[0].init)) return this.parseForIn(node, _init);
+    return this.parseFor(node, _init);
+  }
+  var refShorthandDefaultPos = { start: 0 };
+  var init = this.parseExpression(true, refShorthandDefaultPos);
+  if (this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) {
+    this.toAssignable(init);
+    this.checkLVal(init);
+    return this.parseForIn(node, init);
+  } else if (refShorthandDefaultPos.start) {
+    this.unexpected(refShorthandDefaultPos.start);
+  }
+  return this.parseFor(node, init);
+};
+
+pp.parseFunctionStatement = function (node) {
+  this.next();
+  return this.parseFunction(node, true);
+};
+
+pp.parseIfStatement = function (node) {
+  this.next();
+  node.test = this.parseParenExpression();
+  node.consequent = this.parseStatement(false);
+  node.alternate = this.eat(_tokentype.types._else) ? this.parseStatement(false) : null;
+  return this.finishNode(node, "IfStatement");
+};
+
+pp.parseReturnStatement = function (node) {
+  if (!this.inFunction && !this.options.allowReturnOutsideFunction) this.raise(this.start, "'return' outside of function");
+  this.next();
+
+  // In `return` (and `break`/`continue`), the keywords with
+  // optional arguments, we eagerly look for a semicolon or the
+  // possibility to insert one.
+
+  if (this.eat(_tokentype.types.semi) || this.insertSemicolon()) node.argument = null;else {
+    node.argument = this.parseExpression();this.semicolon();
+  }
+  return this.finishNode(node, "ReturnStatement");
+};
+
+pp.parseSwitchStatement = function (node) {
+  this.next();
+  node.discriminant = this.parseParenExpression();
+  node.cases = [];
+  this.expect(_tokentype.types.braceL);
+  this.labels.push(switchLabel);
+
+  // Statements under must be grouped (by label) in SwitchCase
+  // nodes. `cur` is used to keep the node that we are currently
+  // adding statements to.
+
+  for (var cur, sawDefault = false; this.type != _tokentype.types.braceR;) {
+    if (this.type === _tokentype.types._case || this.type === _tokentype.types._default) {
+      var isCase = this.type === _tokentype.types._case;
+      if (cur) this.finishNode(cur, "SwitchCase");
+      node.cases.push(cur = this.startNode());
+      cur.consequent = [];
+      this.next();
+      if (isCase) {
+        cur.test = this.parseExpression();
+      } else {
+        if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses");
+        sawDefault = true;
+        cur.test = null;
+      }
+      this.expect(_tokentype.types.colon);
+    } else {
+      if (!cur) this.unexpected();
+      cur.consequent.push(this.parseStatement(true));
+    }
+  }
+  if (cur) this.finishNode(cur, "SwitchCase");
+  this.next(); // Closing brace
+  this.labels.pop();
+  return this.finishNode(node, "SwitchStatement");
+};
+
+pp.parseThrowStatement = function (node) {
+  this.next();
+  if (_whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) this.raise(this.lastTokEnd, "Illegal newline after throw");
+  node.argument = this.parseExpression();
+  this.semicolon();
+  return this.finishNode(node, "ThrowStatement");
+};
+
+// Reused empty array added for node fields that are always empty.
+
+var empty = [];
+
+pp.parseTryStatement = function (node) {
+  this.next();
+  node.block = this.parseBlock();
+  node.handler = null;
+  if (this.type === _tokentype.types._catch) {
+    var clause = this.startNode();
+    this.next();
+    this.expect(_tokentype.types.parenL);
+    clause.param = this.parseBindingAtom();
+    this.checkLVal(clause.param, true);
+    this.expect(_tokentype.types.parenR);
+    clause.guard = null;
+    clause.body = this.parseBlock();
+    node.handler = this.finishNode(clause, "CatchClause");
+  }
+  node.guardedHandlers = empty;
+  node.finalizer = this.eat(_tokentype.types._finally) ? this.parseBlock() : null;
+  if (!node.handler && !node.finalizer) this.raise(node.start, "Missing catch or finally clause");
+  return this.finishNode(node, "TryStatement");
+};
+
+pp.parseVarStatement = function (node, kind) {
+  this.next();
+  this.parseVar(node, false, kind);
+  this.semicolon();
+  return this.finishNode(node, "VariableDeclaration");
+};
+
+pp.parseWhileStatement = function (node) {
+  this.next();
+  node.test = this.parseParenExpression();
+  this.labels.push(loopLabel);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  return this.finishNode(node, "WhileStatement");
+};
+
+pp.parseWithStatement = function (node) {
+  if (this.strict) this.raise(this.start, "'with' in strict mode");
+  this.next();
+  node.object = this.parseParenExpression();
+  node.body = this.parseStatement(false);
+  return this.finishNode(node, "WithStatement");
+};
+
+pp.parseEmptyStatement = function (node) {
+  this.next();
+  return this.finishNode(node, "EmptyStatement");
+};
+
+pp.parseLabeledStatement = function (node, maybeName, expr) {
+  for (var i = 0; i < this.labels.length; ++i) {
+    if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared");
+  }var kind = this.type.isLoop ? "loop" : this.type === _tokentype.types._switch ? "switch" : null;
+  for (var i = this.labels.length - 1; i >= 0; i--) {
+    var label = this.labels[i];
+    if (label.statementStart == node.start) {
+      label.statementStart = this.start;
+      label.kind = kind;
+    } else break;
+  }
+  this.labels.push({ name: maybeName, kind: kind, statementStart: this.start });
+  node.body = this.parseStatement(true);
+  this.labels.pop();
+  node.label = expr;
+  return this.finishNode(node, "LabeledStatement");
+};
+
+pp.parseExpressionStatement = function (node, expr) {
+  node.expression = expr;
+  this.semicolon();
+  return this.finishNode(node, "ExpressionStatement");
+};
+
+// Parse a semicolon-enclosed block of statements, handling `"use
+// strict"` declarations when `allowStrict` is true (used for
+// function bodies).
+
+pp.parseBlock = function (allowStrict) {
+  var node = this.startNode(),
+      first = true,
+      oldStrict = undefined;
+  node.body = [];
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    var stmt = this.parseStatement(true);
+    node.body.push(stmt);
+    if (first && allowStrict && this.isUseStrict(stmt)) {
+      oldStrict = this.strict;
+      this.setStrict(this.strict = true);
+    }
+    first = false;
+  }
+  if (oldStrict === false) this.setStrict(false);
+  return this.finishNode(node, "BlockStatement");
+};
+
+// Parse a regular `for` loop. The disambiguation code in
+// `parseStatement` will already have parsed the init statement or
+// expression.
+
+pp.parseFor = function (node, init) {
+  node.init = init;
+  this.expect(_tokentype.types.semi);
+  node.test = this.type === _tokentype.types.semi ? null : this.parseExpression();
+  this.expect(_tokentype.types.semi);
+  node.update = this.type === _tokentype.types.parenR ? null : this.parseExpression();
+  this.expect(_tokentype.types.parenR);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  return this.finishNode(node, "ForStatement");
+};
+
+// Parse a `for`/`in` and `for`/`of` loop, which are almost
+// same from parser's perspective.
+
+pp.parseForIn = function (node, init) {
+  var type = this.type === _tokentype.types._in ? "ForInStatement" : "ForOfStatement";
+  this.next();
+  node.left = init;
+  node.right = this.parseExpression();
+  this.expect(_tokentype.types.parenR);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  return this.finishNode(node, type);
+};
+
+// Parse a list of variable declarations.
+
+pp.parseVar = function (node, isFor, kind) {
+  node.declarations = [];
+  node.kind = kind.keyword;
+  for (;;) {
+    var decl = this.startNode();
+    this.parseVarId(decl);
+    if (this.eat(_tokentype.types.eq)) {
+      decl.init = this.parseMaybeAssign(isFor);
+    } else if (kind === _tokentype.types._const && !(this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
+      this.unexpected();
+    } else if (decl.id.type != "Identifier" && !(isFor && (this.type === _tokentype.types._in || this.isContextual("of")))) {
+      this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value");
+    } else {
+      decl.init = null;
+    }
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+    if (!this.eat(_tokentype.types.comma)) break;
+  }
+  return node;
+};
+
+pp.parseVarId = function (decl) {
+  decl.id = this.parseBindingAtom();
+  this.checkLVal(decl.id, true);
+};
+
+// Parse a function declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseFunction = function (node, isStatement, allowExpressionBody) {
+  this.initFunction(node);
+  if (this.options.ecmaVersion >= 6) node.generator = this.eat(_tokentype.types.star);
+  if (isStatement || this.type === _tokentype.types.name) node.id = this.parseIdent();
+  this.parseFunctionParams(node);
+  this.parseFunctionBody(node, allowExpressionBody);
+  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+};
+
+pp.parseFunctionParams = function (node) {
+  this.expect(_tokentype.types.parenL);
+  node.params = this.parseBindingList(_tokentype.types.parenR, false, false);
+};
+
+// Parse a class declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseClass = function (node, isStatement) {
+  this.next();
+  this.parseClassId(node, isStatement);
+  this.parseClassSuper(node);
+  var classBody = this.startNode();
+  var hadConstructor = false;
+  classBody.body = [];
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (this.eat(_tokentype.types.semi)) continue;
+    var method = this.startNode();
+    var isGenerator = this.eat(_tokentype.types.star);
+    var isMaybeStatic = this.type === _tokentype.types.name && this.value === "static";
+    this.parsePropertyName(method);
+    method["static"] = isMaybeStatic && this.type !== _tokentype.types.parenL;
+    if (method["static"]) {
+      if (isGenerator) this.unexpected();
+      isGenerator = this.eat(_tokentype.types.star);
+      this.parsePropertyName(method);
+    }
+    method.kind = "method";
+    var isGetSet = false;
+    if (!method.computed) {
+      var key = method.key;
+
+      if (!isGenerator && key.type === "Identifier" && this.type !== _tokentype.types.parenL && (key.name === "get" || key.name === "set")) {
+        isGetSet = true;
+        method.kind = key.name;
+        key = this.parsePropertyName(method);
+      }
+      if (!method["static"] && (key.type === "Identifier" && key.name === "constructor" || key.type === "Literal" && key.value === "constructor")) {
+        if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class");
+        if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier");
+        if (isGenerator) this.raise(key.start, "Constructor can't be a generator");
+        method.kind = "constructor";
+        hadConstructor = true;
+      }
+    }
+    this.parseClassMethod(classBody, method, isGenerator);
+    if (isGetSet) {
+      var paramCount = method.kind === "get" ? 0 : 1;
+      if (method.value.params.length !== paramCount) {
+        var start = method.value.start;
+        if (method.kind === "get") this.raise(start, "getter should have no params");else this.raise(start, "setter should have exactly one param");
+      }
+    }
+  }
+  node.body = this.finishNode(classBody, "ClassBody");
+  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
+};
+
+pp.parseClassMethod = function (classBody, method, isGenerator) {
+  method.value = this.parseMethod(isGenerator);
+  classBody.body.push(this.finishNode(method, "MethodDefinition"));
+};
+
+pp.parseClassId = function (node, isStatement) {
+  node.id = this.type === _tokentype.types.name ? this.parseIdent() : isStatement ? this.unexpected() : null;
+};
+
+pp.parseClassSuper = function (node) {
+  node.superClass = this.eat(_tokentype.types._extends) ? this.parseExprSubscripts() : null;
+};
+
+// Parses module export declaration.
+
+pp.parseExport = function (node) {
+  this.next();
+  // export * from '...'
+  if (this.eat(_tokentype.types.star)) {
+    this.expectContextual("from");
+    node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
+    this.semicolon();
+    return this.finishNode(node, "ExportAllDeclaration");
+  }
+  if (this.eat(_tokentype.types._default)) {
+    // export default ...
+    var expr = this.parseMaybeAssign();
+    var needsSemi = true;
+    if (expr.type == "FunctionExpression" || expr.type == "ClassExpression") {
+      needsSemi = false;
+      if (expr.id) {
+        expr.type = expr.type == "FunctionExpression" ? "FunctionDeclaration" : "ClassDeclaration";
+      }
+    }
+    node.declaration = expr;
+    if (needsSemi) this.semicolon();
+    return this.finishNode(node, "ExportDefaultDeclaration");
+  }
+  // export var|const|let|function|class ...
+  if (this.shouldParseExportStatement()) {
+    node.declaration = this.parseStatement(true);
+    node.specifiers = [];
+    node.source = null;
+  } else {
+    // export { x, y as z } [from '...']
+    node.declaration = null;
+    node.specifiers = this.parseExportSpecifiers();
+    if (this.eatContextual("from")) {
+      node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
+    } else {
+      node.source = null;
+    }
+    this.semicolon();
+  }
+  return this.finishNode(node, "ExportNamedDeclaration");
+};
+
+pp.shouldParseExportStatement = function () {
+  return this.type.keyword;
+};
+
+// Parses a comma-separated list of module exports.
+
+pp.parseExportSpecifiers = function () {
+  var nodes = [],
+      first = true;
+  // export { x, y as z } [from '...']
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
+    } else first = false;
+
+    var node = this.startNode();
+    node.local = this.parseIdent(this.type === _tokentype.types._default);
+    node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local;
+    nodes.push(this.finishNode(node, "ExportSpecifier"));
+  }
+  return nodes;
+};
+
+// Parses import declaration.
+
+pp.parseImport = function (node) {
+  this.next();
+  // import '...'
+  if (this.type === _tokentype.types.string) {
+    node.specifiers = empty;
+    node.source = this.parseExprAtom();
+  } else {
+    node.specifiers = this.parseImportSpecifiers();
+    this.expectContextual("from");
+    node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
+  }
+  this.semicolon();
+  return this.finishNode(node, "ImportDeclaration");
+};
+
+// Parses a comma-separated list of module imports.
+
+pp.parseImportSpecifiers = function () {
+  var nodes = [],
+      first = true;
+  if (this.type === _tokentype.types.name) {
+    // import defaultObj, { x, y as z } from '...'
+    var node = this.startNode();
+    node.local = this.parseIdent();
+    this.checkLVal(node.local, true);
+    nodes.push(this.finishNode(node, "ImportDefaultSpecifier"));
+    if (!this.eat(_tokentype.types.comma)) return nodes;
+  }
+  if (this.type === _tokentype.types.star) {
+    var node = this.startNode();
+    this.next();
+    this.expectContextual("as");
+    node.local = this.parseIdent();
+    this.checkLVal(node.local, true);
+    nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"));
+    return nodes;
+  }
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
+    } else first = false;
+
+    var node = this.startNode();
+    node.imported = this.parseIdent(true);
+    node.local = this.eatContextual("as") ? this.parseIdent() : node.imported;
+    this.checkLVal(node.local, true);
+    nodes.push(this.finishNode(node, "ImportSpecifier"));
+  }
+  return nodes;
+};
+
+},{"./state":10,"./tokentype":14,"./whitespace":16}],12:[function(_dereq_,module,exports){
+// The algorithm used to determine whether a regexp can appear at a
+// given point in the program is loosely based on sweet.js' approach.
+// See https://github.com/mozilla/sweet.js/wiki/design
+
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _state = _dereq_("./state");
+
+var _tokentype = _dereq_("./tokentype");
+
+var _whitespace = _dereq_("./whitespace");
+
+var TokContext = function TokContext(token, isExpr, preserveSpace, override) {
+  _classCallCheck(this, TokContext);
+
+  this.token = token;
+  this.isExpr = !!isExpr;
+  this.preserveSpace = !!preserveSpace;
+  this.override = override;
+};
+
+exports.TokContext = TokContext;
+var types = {
+  b_stat: new TokContext("{", false),
+  b_expr: new TokContext("{", true),
+  b_tmpl: new TokContext("${", true),
+  p_stat: new TokContext("(", false),
+  p_expr: new TokContext("(", true),
+  q_tmpl: new TokContext("`", true, true, function (p) {
+    return p.readTmplToken();
+  }),
+  f_expr: new TokContext("function", true)
+};
+
+exports.types = types;
+var pp = _state.Parser.prototype;
+
+pp.initialContext = function () {
+  return [types.b_stat];
+};
+
+pp.braceIsBlock = function (prevType) {
+  if (prevType === _tokentype.types.colon) {
+    var _parent = this.curContext();
+    if (_parent === types.b_stat || _parent === types.b_expr) return !_parent.isExpr;
+  }
+  if (prevType === _tokentype.types._return) return _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
+  if (prevType === _tokentype.types._else || prevType === _tokentype.types.semi || prevType === _tokentype.types.eof || prevType === _tokentype.types.parenR) return true;
+  if (prevType == _tokentype.types.braceL) return this.curContext() === types.b_stat;
+  return !this.exprAllowed;
+};
+
+pp.updateContext = function (prevType) {
+  var update = undefined,
+      type = this.type;
+  if (type.keyword && prevType == _tokentype.types.dot) this.exprAllowed = false;else if (update = type.updateContext) update.call(this, prevType);else this.exprAllowed = type.beforeExpr;
+};
+
+// Token-specific context update code
+
+_tokentype.types.parenR.updateContext = _tokentype.types.braceR.updateContext = function () {
+  if (this.context.length == 1) {
+    this.exprAllowed = true;
+    return;
+  }
+  var out = this.context.pop();
+  if (out === types.b_stat && this.curContext() === types.f_expr) {
+    this.context.pop();
+    this.exprAllowed = false;
+  } else if (out === types.b_tmpl) {
+    this.exprAllowed = true;
+  } else {
+    this.exprAllowed = !out.isExpr;
+  }
+};
+
+_tokentype.types.braceL.updateContext = function (prevType) {
+  this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);
+  this.exprAllowed = true;
+};
+
+_tokentype.types.dollarBraceL.updateContext = function () {
+  this.context.push(types.b_tmpl);
+  this.exprAllowed = true;
+};
+
+_tokentype.types.parenL.updateContext = function (prevType) {
+  var statementParens = prevType === _tokentype.types._if || prevType === _tokentype.types._for || prevType === _tokentype.types._with || prevType === _tokentype.types._while;
+  this.context.push(statementParens ? types.p_stat : types.p_expr);
+  this.exprAllowed = true;
+};
+
+_tokentype.types.incDec.updateContext = function () {};
+
+_tokentype.types._function.updateContext = function () {
+  if (this.curContext() !== types.b_stat) this.context.push(types.f_expr);
+  this.exprAllowed = false;
+};
+
+_tokentype.types.backQuote.updateContext = function () {
+  if (this.curContext() === types.q_tmpl) this.context.pop();else this.context.push(types.q_tmpl);
+  this.exprAllowed = false;
+};
+
+// tokExprAllowed stays unchanged
+
+},{"./state":10,"./tokentype":14,"./whitespace":16}],13:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _identifier = _dereq_("./identifier");
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _locutil = _dereq_("./locutil");
+
+var _whitespace = _dereq_("./whitespace");
+
+// Object type used to represent tokens. Note that normally, tokens
+// simply exist as properties on the parser object. This is only
+// used for the onToken callback and the external tokenizer.
+
+var Token = function Token(p) {
+  _classCallCheck(this, Token);
+
+  this.type = p.type;
+  this.value = p.value;
+  this.start = p.start;
+  this.end = p.end;
+  if (p.options.locations) this.loc = new _locutil.SourceLocation(p, p.startLoc, p.endLoc);
+  if (p.options.ranges) this.range = [p.start, p.end];
+};
+
+exports.Token = Token;
+
+// ## Tokenizer
+
+var pp = _state.Parser.prototype;
+
+// Are we running under Rhino?
+var isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]";
+
+// Move to the next token
+
+pp.next = function () {
+  if (this.options.onToken) this.options.onToken(new Token(this));
+
+  this.lastTokEnd = this.end;
+  this.lastTokStart = this.start;
+  this.lastTokEndLoc = this.endLoc;
+  this.lastTokStartLoc = this.startLoc;
+  this.nextToken();
+};
+
+pp.getToken = function () {
+  this.next();
+  return new Token(this);
+};
+
+// If we're in an ES6 environment, make parsers iterable
+if (typeof Symbol !== "undefined") pp[Symbol.iterator] = function () {
+  var self = this;
+  return { next: function next() {
+      var token = self.getToken();
+      return {
+        done: token.type === _tokentype.types.eof,
+        value: token
+      };
+    } };
+};
+
+// Toggle strict mode. Re-reads the next number or string to please
+// pedantic tests (`"use strict"; 010;` should fail).
+
+pp.setStrict = function (strict) {
+  this.strict = strict;
+  if (this.type !== _tokentype.types.num && this.type !== _tokentype.types.string) return;
+  this.pos = this.start;
+  if (this.options.locations) {
+    while (this.pos < this.lineStart) {
+      this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
+      --this.curLine;
+    }
+  }
+  this.nextToken();
+};
+
+pp.curContext = function () {
+  return this.context[this.context.length - 1];
+};
+
+// Read a single token, updating the parser object's token-related
+// properties.
+
+pp.nextToken = function () {
+  var curContext = this.curContext();
+  if (!curContext || !curContext.preserveSpace) this.skipSpace();
+
+  this.start = this.pos;
+  if (this.options.locations) this.startLoc = this.curPosition();
+  if (this.pos >= this.input.length) return this.finishToken(_tokentype.types.eof);
+
+  if (curContext.override) return curContext.override(this);else this.readToken(this.fullCharCodeAtPos());
+};
+
+pp.readToken = function (code) {
+  // Identifier or keyword. '\uXXXX' sequences are allowed in
+  // identifiers, so '\' also dispatches to that.
+  if (_identifier.isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) return this.readWord();
+
+  return this.getTokenFromCode(code);
+};
+
+pp.fullCharCodeAtPos = function () {
+  var code = this.input.charCodeAt(this.pos);
+  if (code <= 0xd7ff || code >= 0xe000) return code;
+  var next = this.input.charCodeAt(this.pos + 1);
+  return (code << 10) + next - 0x35fdc00;
+};
+
+pp.skipBlockComment = function () {
+  var startLoc = this.options.onComment && this.curPosition();
+  var start = this.pos,
+      end = this.input.indexOf("*/", this.pos += 2);
+  if (end === -1) this.raise(this.pos - 2, "Unterminated comment");
+  this.pos = end + 2;
+  if (this.options.locations) {
+    _whitespace.lineBreakG.lastIndex = start;
+    var match = undefined;
+    while ((match = _whitespace.lineBreakG.exec(this.input)) && match.index < this.pos) {
+      ++this.curLine;
+      this.lineStart = match.index + match[0].length;
+    }
+  }
+  if (this.options.onComment) this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, startLoc, this.curPosition());
+};
+
+pp.skipLineComment = function (startSkip) {
+  var start = this.pos;
+  var startLoc = this.options.onComment && this.curPosition();
+  var ch = this.input.charCodeAt(this.pos += startSkip);
+  while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
+    ++this.pos;
+    ch = this.input.charCodeAt(this.pos);
+  }
+  if (this.options.onComment) this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.curPosition());
+};
+
+// Called at the start of the parse and after every token. Skips
+// whitespace and comments, and.
+
+pp.skipSpace = function () {
+  loop: while (this.pos < this.input.length) {
+    var ch = this.input.charCodeAt(this.pos);
+    switch (ch) {
+      case 32:case 160:
+        // ' '
+        ++this.pos;
+        break;
+      case 13:
+        if (this.input.charCodeAt(this.pos + 1) === 10) {
+          ++this.pos;
+        }
+      case 10:case 8232:case 8233:
+        ++this.pos;
+        if (this.options.locations) {
+          ++this.curLine;
+          this.lineStart = this.pos;
+        }
+        break;
+      case 47:
+        // '/'
+        switch (this.input.charCodeAt(this.pos + 1)) {
+          case 42:
+            // '*'
+            this.skipBlockComment();
+            break;
+          case 47:
+            this.skipLineComment(2);
+            break;
+          default:
+            break loop;
+        }
+        break;
+      default:
+        if (ch > 8 && ch < 14 || ch >= 5760 && _whitespace.nonASCIIwhitespace.test(String.fromCharCode(ch))) {
+          ++this.pos;
+        } else {
+          break loop;
+        }
+    }
+  }
+};
+
+// Called at the end of every token. Sets `end`, `val`, and
+// maintains `context` and `exprAllowed`, and skips the space after
+// the token, so that the next one's `start` will point at the
+// right position.
+
+pp.finishToken = function (type, val) {
+  this.end = this.pos;
+  if (this.options.locations) this.endLoc = this.curPosition();
+  var prevType = this.type;
+  this.type = type;
+  this.value = val;
+
+  this.updateContext(prevType);
+};
+
+// ### Token reading
+
+// This is the function that is called to fetch the next token. It
+// is somewhat obscure, because it works in character codes rather
+// than characters, and because operator parsing has been inlined
+// into it.
+//
+// All in the name of speed.
+//
+pp.readToken_dot = function () {
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next >= 48 && next <= 57) return this.readNumber(true);
+  var next2 = this.input.charCodeAt(this.pos + 2);
+  if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) {
+    // 46 = dot '.'
+    this.pos += 3;
+    return this.finishToken(_tokentype.types.ellipsis);
+  } else {
+    ++this.pos;
+    return this.finishToken(_tokentype.types.dot);
+  }
+};
+
+pp.readToken_slash = function () {
+  // '/'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (this.exprAllowed) {
+    ++this.pos;return this.readRegexp();
+  }
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(_tokentype.types.slash, 1);
+};
+
+pp.readToken_mult_modulo = function (code) {
+  // '%*'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(code === 42 ? _tokentype.types.star : _tokentype.types.modulo, 1);
+};
+
+pp.readToken_pipe_amp = function (code) {
+  // '|&'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === code) return this.finishOp(code === 124 ? _tokentype.types.logicalOR : _tokentype.types.logicalAND, 2);
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(code === 124 ? _tokentype.types.bitwiseOR : _tokentype.types.bitwiseAND, 1);
+};
+
+pp.readToken_caret = function () {
+  // '^'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(_tokentype.types.bitwiseXOR, 1);
+};
+
+pp.readToken_plus_min = function (code) {
+  // '+-'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === code) {
+    if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) {
+      // A `-->` line comment
+      this.skipLineComment(3);
+      this.skipSpace();
+      return this.nextToken();
+    }
+    return this.finishOp(_tokentype.types.incDec, 2);
+  }
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(_tokentype.types.plusMin, 1);
+};
+
+pp.readToken_lt_gt = function (code) {
+  // '<>'
+  var next = this.input.charCodeAt(this.pos + 1);
+  var size = 1;
+  if (next === code) {
+    size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
+    if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(_tokentype.types.assign, size + 1);
+    return this.finishOp(_tokentype.types.bitShift, size);
+  }
+  if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) {
+    if (this.inModule) this.unexpected();
+    // `<!--`, an XML-style comment that should be interpreted as a line comment
+    this.skipLineComment(4);
+    this.skipSpace();
+    return this.nextToken();
+  }
+  if (next === 61) size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2;
+  return this.finishOp(_tokentype.types.relational, size);
+};
+
+pp.readToken_eq_excl = function (code) {
+  // '=!'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === 61) return this.finishOp(_tokentype.types.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2);
+  if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) {
+    // '=>'
+    this.pos += 2;
+    return this.finishToken(_tokentype.types.arrow);
+  }
+  return this.finishOp(code === 61 ? _tokentype.types.eq : _tokentype.types.prefix, 1);
+};
+
+pp.getTokenFromCode = function (code) {
+  switch (code) {
+    // The interpretation of a dot depends on whether it is followed
+    // by a digit or another two dots.
+    case 46:
+      // '.'
+      return this.readToken_dot();
+
+    // Punctuation tokens.
+    case 40:
+      ++this.pos;return this.finishToken(_tokentype.types.parenL);
+    case 41:
+      ++this.pos;return this.finishToken(_tokentype.types.parenR);
+    case 59:
+      ++this.pos;return this.finishToken(_tokentype.types.semi);
+    case 44:
+      ++this.pos;return this.finishToken(_tokentype.types.comma);
+    case 91:
+      ++this.pos;return this.finishToken(_tokentype.types.bracketL);
+    case 93:
+      ++this.pos;return this.finishToken(_tokentype.types.bracketR);
+    case 123:
+      ++this.pos;return this.finishToken(_tokentype.types.braceL);
+    case 125:
+      ++this.pos;return this.finishToken(_tokentype.types.braceR);
+    case 58:
+      ++this.pos;return this.finishToken(_tokentype.types.colon);
+    case 63:
+      ++this.pos;return this.finishToken(_tokentype.types.question);
+
+    case 96:
+      // '`'
+      if (this.options.ecmaVersion < 6) break;
+      ++this.pos;
+      return this.finishToken(_tokentype.types.backQuote);
+
+    case 48:
+      // '0'
+      var next = this.input.charCodeAt(this.pos + 1);
+      if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
+      if (this.options.ecmaVersion >= 6) {
+        if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
+        if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
+      }
+    // Anything else beginning with a digit is an integer, octal
+    // number, or float.
+    case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:
+      // 1-9
+      return this.readNumber(false);
+
+    // Quotes produce strings.
+    case 34:case 39:
+      // '"', "'"
+      return this.readString(code);
+
+    // Operators are parsed inline in tiny state machines. '=' (61) is
+    // often referred to. `finishOp` simply skips the amount of
+    // characters it is given as second argument, and returns a token
+    // of the type given by its first argument.
+
+    case 47:
+      // '/'
+      return this.readToken_slash();
+
+    case 37:case 42:
+      // '%*'
+      return this.readToken_mult_modulo(code);
+
+    case 124:case 38:
+      // '|&'
+      return this.readToken_pipe_amp(code);
+
+    case 94:
+      // '^'
+      return this.readToken_caret();
+
+    case 43:case 45:
+      // '+-'
+      return this.readToken_plus_min(code);
+
+    case 60:case 62:
+      // '<>'
+      return this.readToken_lt_gt(code);
+
+    case 61:case 33:
+      // '=!'
+      return this.readToken_eq_excl(code);
+
+    case 126:
+      // '~'
+      return this.finishOp(_tokentype.types.prefix, 1);
+  }
+
+  this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
+};
+
+pp.finishOp = function (type, size) {
+  var str = this.input.slice(this.pos, this.pos + size);
+  this.pos += size;
+  return this.finishToken(type, str);
+};
+
+// Parse a regular expression. Some context-awareness is necessary,
+// since a '/' inside a '[]' set does not end the expression.
+
+function tryCreateRegexp(src, flags, throwErrorAt) {
+  try {
+    return new RegExp(src, flags);
+  } catch (e) {
+    if (throwErrorAt !== undefined) {
+      if (e instanceof SyntaxError) this.raise(throwErrorAt, "Error parsing regular expression: " + e.message);
+      this.raise(e);
+    }
+  }
+}
+
+var regexpUnicodeSupport = !!tryCreateRegexp("", "u");
+
+pp.readRegexp = function () {
+  var _this = this;
+
+  var escaped = undefined,
+      inClass = undefined,
+      start = this.pos;
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
+    var ch = this.input.charAt(this.pos);
+    if (_whitespace.lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
+    if (!escaped) {
+      if (ch === "[") inClass = true;else if (ch === "]" && inClass) inClass = false;else if (ch === "/" && !inClass) break;
+      escaped = ch === "\\";
+    } else escaped = false;
+    ++this.pos;
+  }
+  var content = this.input.slice(start, this.pos);
+  ++this.pos;
+  // Need to use `readWord1` because '\uXXXX' sequences are allowed
+  // here (don't ask).
+  var mods = this.readWord1();
+  var tmp = content;
+  if (mods) {
+    var validFlags = /^[gmsiy]*$/;
+    if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/;
+    if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag");
+    if (mods.indexOf("u") >= 0 && !regexpUnicodeSupport) {
+      // Replace each astral symbol and every Unicode escape sequence that
+      // possibly represents an astral symbol or a paired surrogate with a
+      // single ASCII symbol to avoid throwing on regular expressions that
+      // are only valid in combination with the `/u` flag.
+      // Note: replacing with the ASCII symbol `x` might cause false
+      // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
+      // perfectly valid pattern that is equivalent to `[a-b]`, but it would
+      // be replaced by `[x-b]` which throws an error.
+      tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, function (match, code, offset) {
+        code = Number("0x" + code);
+        if (code > 0x10FFFF) _this.raise(start + offset + 3, "Code point out of bounds");
+        return "x";
+      });
+      tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
+    }
+  }
+  // Detect invalid regular expressions.
+  var value = null;
+  // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
+  // so don't do detection if we are running under Rhino
+  if (!isRhino) {
+    tryCreateRegexp(tmp, undefined, start);
+    // Get a regular expression object for this pattern-flag pair, or `null` in
+    // case the current environment doesn't support the flags it uses.
+    value = tryCreateRegexp(content, mods);
+  }
+  return this.finishToken(_tokentype.types.regexp, { pattern: content, flags: mods, value: value });
+};
+
+// Read an integer in the given radix. Return null if zero digits
+// were read, the integer value otherwise. When `len` is given, this
+// will return `null` unless the integer has exactly `len` digits.
+
+pp.readInt = function (radix, len) {
+  var start = this.pos,
+      total = 0;
+  for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
+    var code = this.input.charCodeAt(this.pos),
+        val = undefined;
+    if (code >= 97) val = code - 97 + 10; // a
+    else if (code >= 65) val = code - 65 + 10; // A
+    else if (code >= 48 && code <= 57) val = code - 48; // 0-9
+    else val = Infinity;
+    if (val >= radix) break;
+    ++this.pos;
+    total = total * radix + val;
+  }
+  if (this.pos === start || len != null && this.pos - start !== len) return null;
+
+  return total;
+};
+
+pp.readRadixNumber = function (radix) {
+  this.pos += 2; // 0x
+  var val = this.readInt(radix);
+  if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix);
+  if (_identifier.isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
+  return this.finishToken(_tokentype.types.num, val);
+};
+
+// Read an integer, octal integer, or floating-point number.
+
+pp.readNumber = function (startsWithDot) {
+  var start = this.pos,
+      isFloat = false,
+      octal = this.input.charCodeAt(this.pos) === 48;
+  if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number");
+  var next = this.input.charCodeAt(this.pos);
+  if (next === 46) {
+    // '.'
+    ++this.pos;
+    this.readInt(10);
+    isFloat = true;
+    next = this.input.charCodeAt(this.pos);
+  }
+  if (next === 69 || next === 101) {
+    // 'eE'
+    next = this.input.charCodeAt(++this.pos);
+    if (next === 43 || next === 45) ++this.pos; // '+-'
+    if (this.readInt(10) === null) this.raise(start, "Invalid number");
+    isFloat = true;
+  }
+  if (_identifier.isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
+
+  var str = this.input.slice(start, this.pos),
+      val = undefined;
+  if (isFloat) val = parseFloat(str);else if (!octal || str.length === 1) val = parseInt(str, 10);else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number");else val = parseInt(str, 8);
+  return this.finishToken(_tokentype.types.num, val);
+};
+
+// Read a string value, interpreting backslash-escapes.
+
+pp.readCodePoint = function () {
+  var ch = this.input.charCodeAt(this.pos),
+      code = undefined;
+
+  if (ch === 123) {
+    if (this.options.ecmaVersion < 6) this.unexpected();
+    var codePos = ++this.pos;
+    code = this.readHexChar(this.input.indexOf("}", this.pos) - this.pos);
+    ++this.pos;
+    if (code > 0x10FFFF) this.raise(codePos, "Code point out of bounds");
+  } else {
+    code = this.readHexChar(4);
+  }
+  return code;
+};
+
+function codePointToString(code) {
+  // UTF-16 Decoding
+  if (code <= 0xFFFF) return String.fromCharCode(code);
+  code -= 0x10000;
+  return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00);
+}
+
+pp.readString = function (quote) {
+  var out = "",
+      chunkStart = ++this.pos;
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant");
+    var ch = this.input.charCodeAt(this.pos);
+    if (ch === quote) break;
+    if (ch === 92) {
+      // '\'
+      out += this.input.slice(chunkStart, this.pos);
+      out += this.readEscapedChar(false);
+      chunkStart = this.pos;
+    } else {
+      if (_whitespace.isNewLine(ch)) this.raise(this.start, "Unterminated string constant");
+      ++this.pos;
+    }
+  }
+  out += this.input.slice(chunkStart, this.pos++);
+  return this.finishToken(_tokentype.types.string, out);
+};
+
+// Reads template string tokens.
+
+pp.readTmplToken = function () {
+  var out = "",
+      chunkStart = this.pos;
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template");
+    var ch = this.input.charCodeAt(this.pos);
+    if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) {
+      // '`', '${'
+      if (this.pos === this.start && this.type === _tokentype.types.template) {
+        if (ch === 36) {
+          this.pos += 2;
+          return this.finishToken(_tokentype.types.dollarBraceL);
+        } else {
+          ++this.pos;
+          return this.finishToken(_tokentype.types.backQuote);
+        }
+      }
+      out += this.input.slice(chunkStart, this.pos);
+      return this.finishToken(_tokentype.types.template, out);
+    }
+    if (ch === 92) {
+      // '\'
+      out += this.input.slice(chunkStart, this.pos);
+      out += this.readEscapedChar(true);
+      chunkStart = this.pos;
+    } else if (_whitespace.isNewLine(ch)) {
+      out += this.input.slice(chunkStart, this.pos);
+      ++this.pos;
+      switch (ch) {
+        case 13:
+          if (this.input.charCodeAt(this.pos) === 10) ++this.pos;
+        case 10:
+          out += "\n";
+          break;
+        default:
+          out += String.fromCharCode(ch);
+          break;
+      }
+      if (this.options.locations) {
+        ++this.curLine;
+        this.lineStart = this.pos;
+      }
+      chunkStart = this.pos;
+    } else {
+      ++this.pos;
+    }
+  }
+};
+
+// Used to read escaped characters
+
+pp.readEscapedChar = function (inTemplate) {
+  var ch = this.input.charCodeAt(++this.pos);
+  ++this.pos;
+  switch (ch) {
+    case 110:
+      return "\n"; // 'n' -> '\n'
+    case 114:
+      return "\r"; // 'r' -> '\r'
+    case 120:
+      return String.fromCharCode(this.readHexChar(2)); // 'x'
+    case 117:
+      return codePointToString(this.readCodePoint()); // 'u'
+    case 116:
+      return "\t"; // 't' -> '\t'
+    case 98:
+      return "\b"; // 'b' -> '\b'
+    case 118:
+      return "\u000b"; // 'v' -> '\u000b'
+    case 102:
+      return "\f"; // 'f' -> '\f'
+    case 13:
+      if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
+    case 10:
+      // ' \n'
+      if (this.options.locations) {
+        this.lineStart = this.pos;++this.curLine;
+      }
+      return "";
+    default:
+      if (ch >= 48 && ch <= 55) {
+        var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];
+        var octal = parseInt(octalStr, 8);
+        if (octal > 255) {
+          octalStr = octalStr.slice(0, -1);
+          octal = parseInt(octalStr, 8);
+        }
+        if (octal > 0 && (this.strict || inTemplate)) {
+          this.raise(this.pos - 2, "Octal literal in strict mode");
+        }
+        this.pos += octalStr.length - 1;
+        return String.fromCharCode(octal);
+      }
+      return String.fromCharCode(ch);
+  }
+};
+
+// Used to read character escape sequences ('\x', '\u', '\U').
+
+pp.readHexChar = function (len) {
+  var codePos = this.pos;
+  var n = this.readInt(16, len);
+  if (n === null) this.raise(codePos, "Bad character escape sequence");
+  return n;
+};
+
+// Read an identifier, and return it as a string. Sets `this.containsEsc`
+// to whether the word contained a '\u' escape.
+//
+// Incrementally adds only escaped chars, adding other chunks as-is
+// as a micro-optimization.
+
+pp.readWord1 = function () {
+  this.containsEsc = false;
+  var word = "",
+      first = true,
+      chunkStart = this.pos;
+  var astral = this.options.ecmaVersion >= 6;
+  while (this.pos < this.input.length) {
+    var ch = this.fullCharCodeAtPos();
+    if (_identifier.isIdentifierChar(ch, astral)) {
+      this.pos += ch <= 0xffff ? 1 : 2;
+    } else if (ch === 92) {
+      // "\"
+      this.containsEsc = true;
+      word += this.input.slice(chunkStart, this.pos);
+      var escStart = this.pos;
+      if (this.input.charCodeAt(++this.pos) != 117) // "u"
+        this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX");
+      ++this.pos;
+      var esc = this.readCodePoint();
+      if (!(first ? _identifier.isIdentifierStart : _identifier.isIdentifierChar)(esc, astral)) this.raise(escStart, "Invalid Unicode escape");
+      word += codePointToString(esc);
+      chunkStart = this.pos;
+    } else {
+      break;
+    }
+    first = false;
+  }
+  return word + this.input.slice(chunkStart, this.pos);
+};
+
+// Read an identifier or keyword token. Will check for reserved
+// words when necessary.
+
+pp.readWord = function () {
+  var word = this.readWord1();
+  var type = _tokentype.types.name;
+  if ((this.options.ecmaVersion >= 6 || !this.containsEsc) && this.isKeyword(word)) type = _tokentype.keywords[word];
+  return this.finishToken(type, word);
+};
+
+},{"./identifier":2,"./locutil":5,"./state":10,"./tokentype":14,"./whitespace":16}],14:[function(_dereq_,module,exports){
+// ## Token types
+
+// The assignment of fine-grained, information-carrying type objects
+// allows the tokenizer to store the information it has about a
+// token in a way that is very cheap for the parser to look up.
+
+// All token type variables start with an underscore, to make them
+// easy to recognize.
+
+// The `beforeExpr` property is used to disambiguate between regular
+// expressions and divisions. It is set on all token types that can
+// be followed by an expression (thus, a slash after them would be a
+// regular expression).
+//
+// `isLoop` marks a keyword as starting a loop, which is important
+// to know when parsing a label, in order to allow or disallow
+// continue jumps to that label.
+
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var TokenType = function TokenType(label) {
+  var conf = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+  _classCallCheck(this, TokenType);
+
+  this.label = label;
+  this.keyword = conf.keyword;
+  this.beforeExpr = !!conf.beforeExpr;
+  this.startsExpr = !!conf.startsExpr;
+  this.isLoop = !!conf.isLoop;
+  this.isAssign = !!conf.isAssign;
+  this.prefix = !!conf.prefix;
+  this.postfix = !!conf.postfix;
+  this.binop = conf.binop || null;
+  this.updateContext = null;
+};
+
+exports.TokenType = TokenType;
+
+function binop(name, prec) {
+  return new TokenType(name, { beforeExpr: true, binop: prec });
+}
+var beforeExpr = { beforeExpr: true },
+    startsExpr = { startsExpr: true };
+
+var types = {
+  num: new TokenType("num", startsExpr),
+  regexp: new TokenType("regexp", startsExpr),
+  string: new TokenType("string", startsExpr),
+  name: new TokenType("name", startsExpr),
+  eof: new TokenType("eof"),
+
+  // Punctuation token types.
+  bracketL: new TokenType("[", { beforeExpr: true, startsExpr: true }),
+  bracketR: new TokenType("]"),
+  braceL: new TokenType("{", { beforeExpr: true, startsExpr: true }),
+  braceR: new TokenType("}"),
+  parenL: new TokenType("(", { beforeExpr: true, startsExpr: true }),
+  parenR: new TokenType(")"),
+  comma: new TokenType(",", beforeExpr),
+  semi: new TokenType(";", beforeExpr),
+  colon: new TokenType(":", beforeExpr),
+  dot: new TokenType("."),
+  question: new TokenType("?", beforeExpr),
+  arrow: new TokenType("=>", beforeExpr),
+  template: new TokenType("template"),
+  ellipsis: new TokenType("...", beforeExpr),
+  backQuote: new TokenType("`", startsExpr),
+  dollarBraceL: new TokenType("${", { beforeExpr: true, startsExpr: true }),
+
+  // Operators. These carry several kinds of properties to help the
+  // parser use them properly (the presence of these properties is
+  // what categorizes them as operators).
+  //
+  // `binop`, when present, specifies that this operator is a binary
+  // operator, and will refer to its precedence.
+  //
+  // `prefix` and `postfix` mark the operator as a prefix or postfix
+  // unary operator.
+  //
+  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
+  // binary operators with a very low precedence, that should result
+  // in AssignmentExpression nodes.
+
+  eq: new TokenType("=", { beforeExpr: true, isAssign: true }),
+  assign: new TokenType("_=", { beforeExpr: true, isAssign: true }),
+  incDec: new TokenType("++/--", { prefix: true, postfix: true, startsExpr: true }),
+  prefix: new TokenType("prefix", { beforeExpr: true, prefix: true, startsExpr: true }),
+  logicalOR: binop("||", 1),
+  logicalAND: binop("&&", 2),
+  bitwiseOR: binop("|", 3),
+  bitwiseXOR: binop("^", 4),
+  bitwiseAND: binop("&", 5),
+  equality: binop("==/!=", 6),
+  relational: binop("</>", 7),
+  bitShift: binop("<</>>", 8),
+  plusMin: new TokenType("+/-", { beforeExpr: true, binop: 9, prefix: true, startsExpr: true }),
+  modulo: binop("%", 10),
+  star: binop("*", 10),
+  slash: binop("/", 10)
+};
+
+exports.types = types;
+// Map keyword names to token types.
+
+var keywords = {};
+
+exports.keywords = keywords;
+// Succinct definitions of keyword token types
+function kw(name) {
+  var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+  options.keyword = name;
+  keywords[name] = types["_" + name] = new TokenType(name, options);
+}
+
+kw("break");
+kw("case", beforeExpr);
+kw("catch");
+kw("continue");
+kw("debugger");
+kw("default", beforeExpr);
+kw("do", { isLoop: true });
+kw("else", beforeExpr);
+kw("finally");
+kw("for", { isLoop: true });
+kw("function", startsExpr);
+kw("if");
+kw("return", beforeExpr);
+kw("switch");
+kw("throw", beforeExpr);
+kw("try");
+kw("var");
+kw("let");
+kw("const");
+kw("while", { isLoop: true });
+kw("with");
+kw("new", { beforeExpr: true, startsExpr: true });
+kw("this", startsExpr);
+kw("super", startsExpr);
+kw("class");
+kw("extends", beforeExpr);
+kw("export");
+kw("import");
+kw("yield", { beforeExpr: true, startsExpr: true });
+kw("null", startsExpr);
+kw("true", startsExpr);
+kw("false", startsExpr);
+kw("in", { beforeExpr: true, binop: 7 });
+kw("instanceof", { beforeExpr: true, binop: 7 });
+kw("typeof", { beforeExpr: true, prefix: true, startsExpr: true });
+kw("void", { beforeExpr: true, prefix: true, startsExpr: true });
+kw("delete", { beforeExpr: true, prefix: true, startsExpr: true });
+
+},{}],15:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.isArray = isArray;
+exports.has = has;
+
+function isArray(obj) {
+  return Object.prototype.toString.call(obj) === "[object Array]";
+}
+
+// Checks if an object has a property.
+
+function has(obj, propName) {
+  return Object.prototype.hasOwnProperty.call(obj, propName);
+}
+
+},{}],16:[function(_dereq_,module,exports){
+// Matches a whole line break (where CRLF is considered a single
+// line break). Used to count lines.
+
+"use strict";
+
+exports.__esModule = true;
+exports.isNewLine = isNewLine;
+var lineBreak = /\r\n?|\n|\u2028|\u2029/;
+exports.lineBreak = lineBreak;
+var lineBreakG = new RegExp(lineBreak.source, "g");
+
+exports.lineBreakG = lineBreakG;
+
+function isNewLine(code) {
+  return code === 10 || code === 13 || code === 0x2028 || code == 0x2029;
+}
+
+var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
+exports.nonASCIIwhitespace = nonASCIIwhitespace;
+
+},{}]},{},[3])(3)
+});
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn_csp.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn_csp.js
new file mode 100644
index 0000000..1e9d42d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn_csp.js
@@ -0,0 +1,3284 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.acorn = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+// A recursive descent parser operates by defining functions for all
+// syntactic elements, and recursively calling those, each function
+// advancing the input stream and returning an AST node. Precedence
+// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
+// instead of `(!x)[1]` is handled by the fact that the parser
+// function that parses unary prefix operators is called first, and
+// in turn calls the function that parses `[]` subscripts — that
+// way, it'll receive the node for `x[1]` already parsed, and wraps
+// *that* in the unary operator node.
+//
+// Acorn uses an [operator precedence parser][opp] to handle binary
+// operator precedence, because it is much more compact than using
+// the technique outlined above, which uses different, nesting
+// functions to specify precedence, for all of the ten binary
+// precedence levels that JavaScript defines.
+//
+// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
+
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _identifier = _dereq_("./identifier");
+
+var _util = _dereq_("./util");
+
+var pp = _state.Parser.prototype;
+
+// Check if property name clashes with already added.
+// Object/class getters and setters are not allowed to clash —
+// either with each other or with an init property — and in
+// strict mode, init properties are also not allowed to be repeated.
+
+pp.checkPropClash = function (prop, propHash) {
+  if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) return;
+  var key = prop.key,
+      name = undefined;
+  switch (key.type) {
+    case "Identifier":
+      name = key.name;break;
+    case "Literal":
+      name = String(key.value);break;
+    default:
+      return;
+  }
+  var kind = prop.kind;
+  if (this.options.ecmaVersion >= 6) {
+    if (name === "__proto__" && kind === "init") {
+      if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
+      propHash.proto = true;
+    }
+    return;
+  }
+  var other = undefined;
+  if (_util.has(propHash, name)) {
+    other = propHash[name];
+    var isGetSet = kind !== "init";
+    if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init)) this.raise(key.start, "Redefinition of property");
+  } else {
+    other = propHash[name] = {
+      init: false,
+      get: false,
+      set: false
+    };
+  }
+  other[kind] = true;
+};
+
+// ### Expression parsing
+
+// These nest, from the most general expression type at the top to
+// 'atomic', nondivisible expression types at the bottom. Most of
+// the functions will simply let the function(s) below them parse,
+// and, *if* the syntactic construct they handle is present, wrap
+// the AST node that the inner parser gave them in another node.
+
+// Parse a full expression. The optional arguments are used to
+// forbid the `in` operator (in for loops initalization expressions)
+// and provide reference for storing '=' operator inside shorthand
+// property assignment in contexts where both object expression
+// and object pattern might appear (so it's possible to raise
+// delayed syntax error at correct position).
+
+pp.parseExpression = function (noIn, refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos);
+  if (this.type === _tokentype.types.comma) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.expressions = [expr];
+    while (this.eat(_tokentype.types.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos));
+    return this.finishNode(node, "SequenceExpression");
+  }
+  return expr;
+};
+
+// Parse an assignment expression. This includes applications of
+// operators like `+=`.
+
+pp.parseMaybeAssign = function (noIn, refShorthandDefaultPos, afterLeftParse) {
+  if (this.type == _tokentype.types._yield && this.inGenerator) return this.parseYield();
+
+  var failOnShorthandAssign = undefined;
+  if (!refShorthandDefaultPos) {
+    refShorthandDefaultPos = { start: 0 };
+    failOnShorthandAssign = true;
+  } else {
+    failOnShorthandAssign = false;
+  }
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  if (this.type == _tokentype.types.parenL || this.type == _tokentype.types.name) this.potentialArrowAt = this.start;
+  var left = this.parseMaybeConditional(noIn, refShorthandDefaultPos);
+  if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
+  if (this.type.isAssign) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.operator = this.value;
+    node.left = this.type === _tokentype.types.eq ? this.toAssignable(left) : left;
+    refShorthandDefaultPos.start = 0; // reset because shorthand default was used correctly
+    this.checkLVal(left);
+    this.next();
+    node.right = this.parseMaybeAssign(noIn);
+    return this.finishNode(node, "AssignmentExpression");
+  } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
+    this.unexpected(refShorthandDefaultPos.start);
+  }
+  return left;
+};
+
+// Parse a ternary conditional (`?:`) operator.
+
+pp.parseMaybeConditional = function (noIn, refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseExprOps(noIn, refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  if (this.eat(_tokentype.types.question)) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.test = expr;
+    node.consequent = this.parseMaybeAssign();
+    this.expect(_tokentype.types.colon);
+    node.alternate = this.parseMaybeAssign(noIn);
+    return this.finishNode(node, "ConditionalExpression");
+  }
+  return expr;
+};
+
+// Start the precedence parser.
+
+pp.parseExprOps = function (noIn, refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseMaybeUnary(refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  return this.parseExprOp(expr, startPos, startLoc, -1, noIn);
+};
+
+// Parse binary operators with the operator precedence parsing
+// algorithm. `left` is the left-hand side of the operator.
+// `minPrec` provides context that allows the function to stop and
+// defer further parser to one of its callers when it encounters an
+// operator that has a lower precedence than the set it is parsing.
+
+pp.parseExprOp = function (left, leftStartPos, leftStartLoc, minPrec, noIn) {
+  var prec = this.type.binop;
+  if (prec != null && (!noIn || this.type !== _tokentype.types._in)) {
+    if (prec > minPrec) {
+      var node = this.startNodeAt(leftStartPos, leftStartLoc);
+      node.left = left;
+      node.operator = this.value;
+      var op = this.type;
+      this.next();
+      var startPos = this.start,
+          startLoc = this.startLoc;
+      node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn);
+      this.finishNode(node, op === _tokentype.types.logicalOR || op === _tokentype.types.logicalAND ? "LogicalExpression" : "BinaryExpression");
+      return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn);
+    }
+  }
+  return left;
+};
+
+// Parse unary operators, both prefix and postfix.
+
+pp.parseMaybeUnary = function (refShorthandDefaultPos) {
+  if (this.type.prefix) {
+    var node = this.startNode(),
+        update = this.type === _tokentype.types.incDec;
+    node.operator = this.value;
+    node.prefix = true;
+    this.next();
+    node.argument = this.parseMaybeUnary();
+    if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
+    if (update) this.checkLVal(node.argument);else if (this.strict && node.operator === "delete" && node.argument.type === "Identifier") this.raise(node.start, "Deleting local variable in strict mode");
+    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+  }
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseExprSubscripts(refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  while (this.type.postfix && !this.canInsertSemicolon()) {
+    var node = this.startNodeAt(startPos, startLoc);
+    node.operator = this.value;
+    node.prefix = false;
+    node.argument = expr;
+    this.checkLVal(expr);
+    this.next();
+    expr = this.finishNode(node, "UpdateExpression");
+  }
+  return expr;
+};
+
+// Parse call, dot, and `[]`-subscript expressions.
+
+pp.parseExprSubscripts = function (refShorthandDefaultPos) {
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  var expr = this.parseExprAtom(refShorthandDefaultPos);
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr;
+  return this.parseSubscripts(expr, startPos, startLoc);
+};
+
+pp.parseSubscripts = function (base, startPos, startLoc, noCalls) {
+  for (;;) {
+    if (this.eat(_tokentype.types.dot)) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.object = base;
+      node.property = this.parseIdent(true);
+      node.computed = false;
+      base = this.finishNode(node, "MemberExpression");
+    } else if (this.eat(_tokentype.types.bracketL)) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.object = base;
+      node.property = this.parseExpression();
+      node.computed = true;
+      this.expect(_tokentype.types.bracketR);
+      base = this.finishNode(node, "MemberExpression");
+    } else if (!noCalls && this.eat(_tokentype.types.parenL)) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.callee = base;
+      node.arguments = this.parseExprList(_tokentype.types.parenR, false);
+      base = this.finishNode(node, "CallExpression");
+    } else if (this.type === _tokentype.types.backQuote) {
+      var node = this.startNodeAt(startPos, startLoc);
+      node.tag = base;
+      node.quasi = this.parseTemplate();
+      base = this.finishNode(node, "TaggedTemplateExpression");
+    } else {
+      return base;
+    }
+  }
+};
+
+// Parse an atomic expression — either a single token that is an
+// expression, an expression started by a keyword like `function` or
+// `new`, or an expression wrapped in punctuation like `()`, `[]`,
+// or `{}`.
+
+pp.parseExprAtom = function (refShorthandDefaultPos) {
+  var node = undefined,
+      canBeArrow = this.potentialArrowAt == this.start;
+  switch (this.type) {
+    case _tokentype.types._super:
+      if (!this.inFunction) this.raise(this.start, "'super' outside of function or class");
+    case _tokentype.types._this:
+      var type = this.type === _tokentype.types._this ? "ThisExpression" : "Super";
+      node = this.startNode();
+      this.next();
+      return this.finishNode(node, type);
+
+    case _tokentype.types._yield:
+      if (this.inGenerator) this.unexpected();
+
+    case _tokentype.types.name:
+      var startPos = this.start,
+          startLoc = this.startLoc;
+      var id = this.parseIdent(this.type !== _tokentype.types.name);
+      if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id]);
+      return id;
+
+    case _tokentype.types.regexp:
+      var value = this.value;
+      node = this.parseLiteral(value.value);
+      node.regex = { pattern: value.pattern, flags: value.flags };
+      return node;
+
+    case _tokentype.types.num:case _tokentype.types.string:
+      return this.parseLiteral(this.value);
+
+    case _tokentype.types._null:case _tokentype.types._true:case _tokentype.types._false:
+      node = this.startNode();
+      node.value = this.type === _tokentype.types._null ? null : this.type === _tokentype.types._true;
+      node.raw = this.type.keyword;
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case _tokentype.types.parenL:
+      return this.parseParenAndDistinguishExpression(canBeArrow);
+
+    case _tokentype.types.bracketL:
+      node = this.startNode();
+      this.next();
+      // check whether this is array comprehension or regular array
+      if (this.options.ecmaVersion >= 7 && this.type === _tokentype.types._for) {
+        return this.parseComprehension(node, false);
+      }
+      node.elements = this.parseExprList(_tokentype.types.bracketR, true, true, refShorthandDefaultPos);
+      return this.finishNode(node, "ArrayExpression");
+
+    case _tokentype.types.braceL:
+      return this.parseObj(false, refShorthandDefaultPos);
+
+    case _tokentype.types._function:
+      node = this.startNode();
+      this.next();
+      return this.parseFunction(node, false);
+
+    case _tokentype.types._class:
+      return this.parseClass(this.startNode(), false);
+
+    case _tokentype.types._new:
+      return this.parseNew();
+
+    case _tokentype.types.backQuote:
+      return this.parseTemplate();
+
+    default:
+      this.unexpected();
+  }
+};
+
+pp.parseLiteral = function (value) {
+  var node = this.startNode();
+  node.value = value;
+  node.raw = this.input.slice(this.start, this.end);
+  this.next();
+  return this.finishNode(node, "Literal");
+};
+
+pp.parseParenExpression = function () {
+  this.expect(_tokentype.types.parenL);
+  var val = this.parseExpression();
+  this.expect(_tokentype.types.parenR);
+  return val;
+};
+
+pp.parseParenAndDistinguishExpression = function (canBeArrow) {
+  var startPos = this.start,
+      startLoc = this.startLoc,
+      val = undefined;
+  if (this.options.ecmaVersion >= 6) {
+    this.next();
+
+    if (this.options.ecmaVersion >= 7 && this.type === _tokentype.types._for) {
+      return this.parseComprehension(this.startNodeAt(startPos, startLoc), true);
+    }
+
+    var innerStartPos = this.start,
+        innerStartLoc = this.startLoc;
+    var exprList = [],
+        first = true;
+    var refShorthandDefaultPos = { start: 0 },
+        spreadStart = undefined,
+        innerParenStart = undefined;
+    while (this.type !== _tokentype.types.parenR) {
+      first ? first = false : this.expect(_tokentype.types.comma);
+      if (this.type === _tokentype.types.ellipsis) {
+        spreadStart = this.start;
+        exprList.push(this.parseParenItem(this.parseRest()));
+        break;
+      } else {
+        if (this.type === _tokentype.types.parenL && !innerParenStart) {
+          innerParenStart = this.start;
+        }
+        exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem));
+      }
+    }
+    var innerEndPos = this.start,
+        innerEndLoc = this.startLoc;
+    this.expect(_tokentype.types.parenR);
+
+    if (canBeArrow && !this.canInsertSemicolon() && this.eat(_tokentype.types.arrow)) {
+      if (innerParenStart) this.unexpected(innerParenStart);
+      return this.parseParenArrowList(startPos, startLoc, exprList);
+    }
+
+    if (!exprList.length) this.unexpected(this.lastTokStart);
+    if (spreadStart) this.unexpected(spreadStart);
+    if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start);
+
+    if (exprList.length > 1) {
+      val = this.startNodeAt(innerStartPos, innerStartLoc);
+      val.expressions = exprList;
+      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc);
+    } else {
+      val = exprList[0];
+    }
+  } else {
+    val = this.parseParenExpression();
+  }
+
+  if (this.options.preserveParens) {
+    var par = this.startNodeAt(startPos, startLoc);
+    par.expression = val;
+    return this.finishNode(par, "ParenthesizedExpression");
+  } else {
+    return val;
+  }
+};
+
+pp.parseParenItem = function (item) {
+  return item;
+};
+
+pp.parseParenArrowList = function (startPos, startLoc, exprList) {
+  return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList);
+};
+
+// New's precedence is slightly tricky. It must allow its argument
+// to be a `[]` or dot subscript expression, but not a call — at
+// least, not without wrapping it in parentheses. Thus, it uses the
+
+var empty = [];
+
+pp.parseNew = function () {
+  var node = this.startNode();
+  var meta = this.parseIdent(true);
+  if (this.options.ecmaVersion >= 6 && this.eat(_tokentype.types.dot)) {
+    node.meta = meta;
+    node.property = this.parseIdent(true);
+    if (node.property.name !== "target") this.raise(node.property.start, "The only valid meta property for new is new.target");
+    return this.finishNode(node, "MetaProperty");
+  }
+  var startPos = this.start,
+      startLoc = this.startLoc;
+  node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true);
+  if (this.eat(_tokentype.types.parenL)) node.arguments = this.parseExprList(_tokentype.types.parenR, false);else node.arguments = empty;
+  return this.finishNode(node, "NewExpression");
+};
+
+// Parse template expression.
+
+pp.parseTemplateElement = function () {
+  var elem = this.startNode();
+  elem.value = {
+    raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, "\n"),
+    cooked: this.value
+  };
+  this.next();
+  elem.tail = this.type === _tokentype.types.backQuote;
+  return this.finishNode(elem, "TemplateElement");
+};
+
+pp.parseTemplate = function () {
+  var node = this.startNode();
+  this.next();
+  node.expressions = [];
+  var curElt = this.parseTemplateElement();
+  node.quasis = [curElt];
+  while (!curElt.tail) {
+    this.expect(_tokentype.types.dollarBraceL);
+    node.expressions.push(this.parseExpression());
+    this.expect(_tokentype.types.braceR);
+    node.quasis.push(curElt = this.parseTemplateElement());
+  }
+  this.next();
+  return this.finishNode(node, "TemplateLiteral");
+};
+
+// Parse an object literal or binding pattern.
+
+pp.parseObj = function (isPattern, refShorthandDefaultPos) {
+  var node = this.startNode(),
+      first = true,
+      propHash = {};
+  node.properties = [];
+  this.next();
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
+    } else first = false;
+
+    var prop = this.startNode(),
+        isGenerator = undefined,
+        startPos = undefined,
+        startLoc = undefined;
+    if (this.options.ecmaVersion >= 6) {
+      prop.method = false;
+      prop.shorthand = false;
+      if (isPattern || refShorthandDefaultPos) {
+        startPos = this.start;
+        startLoc = this.startLoc;
+      }
+      if (!isPattern) isGenerator = this.eat(_tokentype.types.star);
+    }
+    this.parsePropertyName(prop);
+    this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos);
+    this.checkPropClash(prop, propHash);
+    node.properties.push(this.finishNode(prop, "Property"));
+  }
+  return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
+};
+
+pp.parsePropertyValue = function (prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {
+  if (this.eat(_tokentype.types.colon)) {
+    prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
+    prop.kind = "init";
+  } else if (this.options.ecmaVersion >= 6 && this.type === _tokentype.types.parenL) {
+    if (isPattern) this.unexpected();
+    prop.kind = "init";
+    prop.method = true;
+    prop.value = this.parseMethod(isGenerator);
+  } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (this.type != _tokentype.types.comma && this.type != _tokentype.types.braceR)) {
+    if (isGenerator || isPattern) this.unexpected();
+    prop.kind = prop.key.name;
+    this.parsePropertyName(prop);
+    prop.value = this.parseMethod(false);
+    var paramCount = prop.kind === "get" ? 0 : 1;
+    if (prop.value.params.length !== paramCount) {
+      var start = prop.value.start;
+      if (prop.kind === "get") this.raise(start, "getter should have no params");else this.raise(start, "setter should have exactly one param");
+    }
+  } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
+    prop.kind = "init";
+    if (isPattern) {
+      if (this.isKeyword(prop.key.name) || this.strict && (_identifier.reservedWords.strictBind(prop.key.name) || _identifier.reservedWords.strict(prop.key.name)) || !this.options.allowReserved && this.isReservedWord(prop.key.name)) this.raise(prop.key.start, "Binding " + prop.key.name);
+      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
+    } else if (this.type === _tokentype.types.eq && refShorthandDefaultPos) {
+      if (!refShorthandDefaultPos.start) refShorthandDefaultPos.start = this.start;
+      prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key);
+    } else {
+      prop.value = prop.key;
+    }
+    prop.shorthand = true;
+  } else this.unexpected();
+};
+
+pp.parsePropertyName = function (prop) {
+  if (this.options.ecmaVersion >= 6) {
+    if (this.eat(_tokentype.types.bracketL)) {
+      prop.computed = true;
+      prop.key = this.parseMaybeAssign();
+      this.expect(_tokentype.types.bracketR);
+      return prop.key;
+    } else {
+      prop.computed = false;
+    }
+  }
+  return prop.key = this.type === _tokentype.types.num || this.type === _tokentype.types.string ? this.parseExprAtom() : this.parseIdent(true);
+};
+
+// Initialize empty function node.
+
+pp.initFunction = function (node) {
+  node.id = null;
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = false;
+    node.expression = false;
+  }
+};
+
+// Parse object or class method.
+
+pp.parseMethod = function (isGenerator) {
+  var node = this.startNode();
+  this.initFunction(node);
+  this.expect(_tokentype.types.parenL);
+  node.params = this.parseBindingList(_tokentype.types.parenR, false, false);
+  var allowExpressionBody = undefined;
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = isGenerator;
+  }
+  this.parseFunctionBody(node, false);
+  return this.finishNode(node, "FunctionExpression");
+};
+
+// Parse arrow function expression with given parameters.
+
+pp.parseArrowExpression = function (node, params) {
+  this.initFunction(node);
+  node.params = this.toAssignableList(params, true);
+  this.parseFunctionBody(node, true);
+  return this.finishNode(node, "ArrowFunctionExpression");
+};
+
+// Parse function body and check parameters.
+
+pp.parseFunctionBody = function (node, allowExpression) {
+  var isExpression = allowExpression && this.type !== _tokentype.types.braceL;
+
+  if (isExpression) {
+    node.body = this.parseMaybeAssign();
+    node.expression = true;
+  } else {
+    // Start a new scope with regard to labels and the `inFunction`
+    // flag (restore them to their old value afterwards).
+    var oldInFunc = this.inFunction,
+        oldInGen = this.inGenerator,
+        oldLabels = this.labels;
+    this.inFunction = true;this.inGenerator = node.generator;this.labels = [];
+    node.body = this.parseBlock(true);
+    node.expression = false;
+    this.inFunction = oldInFunc;this.inGenerator = oldInGen;this.labels = oldLabels;
+  }
+
+  // If this is a strict mode function, verify that argument names
+  // are not repeated, and it does not try to bind the words `eval`
+  // or `arguments`.
+  if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {
+    var nameHash = {},
+        oldStrict = this.strict;
+    this.strict = true;
+    if (node.id) this.checkLVal(node.id, true);
+    for (var i = 0; i < node.params.length; i++) {
+      this.checkLVal(node.params[i], true, nameHash);
+    }this.strict = oldStrict;
+  }
+};
+
+// Parses a comma-separated list of expressions, and returns them as
+// an array. `close` is the token type that ends the list, and
+// `allowEmpty` can be turned on to allow subsequent commas with
+// nothing in between them to be parsed as `null` (which is needed
+// for array literals).
+
+pp.parseExprList = function (close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
+  var elts = [],
+      first = true;
+  while (!this.eat(close)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (allowTrailingComma && this.afterTrailingComma(close)) break;
+    } else first = false;
+
+    var elt = undefined;
+    if (allowEmpty && this.type === _tokentype.types.comma) elt = null;else if (this.type === _tokentype.types.ellipsis) elt = this.parseSpread(refShorthandDefaultPos);else elt = this.parseMaybeAssign(false, refShorthandDefaultPos);
+    elts.push(elt);
+  }
+  return elts;
+};
+
+// Parse the next token as an identifier. If `liberal` is true (used
+// when parsing properties), it will also convert keywords into
+// identifiers.
+
+pp.parseIdent = function (liberal) {
+  var node = this.startNode();
+  if (liberal && this.options.allowReserved == "never") liberal = false;
+  if (this.type === _tokentype.types.name) {
+    if (!liberal && (!this.options.allowReserved && this.isReservedWord(this.value) || this.strict && _identifier.reservedWords.strict(this.value) && (this.options.ecmaVersion >= 6 || this.input.slice(this.start, this.end).indexOf("\\") == -1))) this.raise(this.start, "The keyword '" + this.value + "' is reserved");
+    node.name = this.value;
+  } else if (liberal && this.type.keyword) {
+    node.name = this.type.keyword;
+  } else {
+    this.unexpected();
+  }
+  this.next();
+  return this.finishNode(node, "Identifier");
+};
+
+// Parses yield expression inside generator.
+
+pp.parseYield = function () {
+  var node = this.startNode();
+  this.next();
+  if (this.type == _tokentype.types.semi || this.canInsertSemicolon() || this.type != _tokentype.types.star && !this.type.startsExpr) {
+    node.delegate = false;
+    node.argument = null;
+  } else {
+    node.delegate = this.eat(_tokentype.types.star);
+    node.argument = this.parseMaybeAssign();
+  }
+  return this.finishNode(node, "YieldExpression");
+};
+
+// Parses array and generator comprehensions.
+
+pp.parseComprehension = function (node, isGenerator) {
+  node.blocks = [];
+  while (this.type === _tokentype.types._for) {
+    var block = this.startNode();
+    this.next();
+    this.expect(_tokentype.types.parenL);
+    block.left = this.parseBindingAtom();
+    this.checkLVal(block.left, true);
+    this.expectContextual("of");
+    block.right = this.parseExpression();
+    this.expect(_tokentype.types.parenR);
+    node.blocks.push(this.finishNode(block, "ComprehensionBlock"));
+  }
+  node.filter = this.eat(_tokentype.types._if) ? this.parseParenExpression() : null;
+  node.body = this.parseExpression();
+  this.expect(isGenerator ? _tokentype.types.parenR : _tokentype.types.bracketR);
+  node.generator = isGenerator;
+  return this.finishNode(node, "ComprehensionExpression");
+};
+
+},{"./identifier":2,"./state":10,"./tokentype":14,"./util":15}],2:[function(_dereq_,module,exports){
+// This is a trick taken from Esprima. It turns out that, on
+// non-Chrome browsers, to check whether a string is in a set, a
+// predicate containing a big ugly `switch` statement is faster than
+// a regular expression, and on Chrome the two are about on par.
+// This function uses `eval` (non-lexical) to produce such a
+// predicate from a space-separated string of words.
+//
+// It starts by sorting the words by length.
+
+"use strict";
+
+exports.__esModule = true;
+exports.isIdentifierStart = isIdentifierStart;
+exports.isIdentifierChar = isIdentifierChar;
+// Removed to create an eval-free library
+
+// Reserved word lists for various dialects of the language
+
+var reservedWords = {
+  3: function anonymous(str) {
+switch(str.length){case 6:switch(str){case "double":case "export":case "import":case "native":case "public":case "static":case "throws":return true}return false;case 4:switch(str){case "byte":case "char":case "enum":case "goto":case "long":return true}return false;case 5:switch(str){case "class":case "final":case "float":case "short":case "super":return true}return false;case 7:switch(str){case "boolean":case "extends":case "package":case "private":return true}return false;case 9:switch(str){case "interface":case "protected":case "transient":return true}return false;case 8:switch(str){case "abstract":case "volatile":return true}return false;case 10:return str === "implements";case 3:return str === "int";case 12:return str === "synchronized";}
+},
+  5: function anonymous(str) {
+switch(str.length){case 5:switch(str){case "class":case "super":case "const":return true}return false;case 6:switch(str){case "export":case "import":return true}return false;case 4:return str === "enum";case 7:return str === "extends";}
+},
+  6: function anonymous(str) {
+switch(str){case "enum":case "await":return true}return false;
+},
+  strict: function anonymous(str) {
+switch(str.length){case 9:switch(str){case "interface":case "protected":return true}return false;case 7:switch(str){case "package":case "private":return true}return false;case 6:switch(str){case "public":case "static":return true}return false;case 10:return str === "implements";case 3:return str === "let";case 5:return str === "yield";}
+},
+  strictBind: function anonymous(str) {
+switch(str){case "eval":case "arguments":return true}return false;
+}
+};
+
+exports.reservedWords = reservedWords;
+// And the keywords
+
+var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";
+
+var keywords = {
+  5: function anonymous(str) {
+switch(str.length){case 4:switch(str){case "case":case "else":case "with":case "null":case "true":case "void":case "this":return true}return false;case 5:switch(str){case "break":case "catch":case "throw":case "while":case "false":return true}return false;case 3:switch(str){case "for":case "try":case "var":case "new":return true}return false;case 6:switch(str){case "return":case "switch":case "typeof":case "delete":return true}return false;case 8:switch(str){case "continue":case "debugger":case "function":return true}return false;case 2:switch(str){case "do":case "if":case "in":return true}return false;case 7:switch(str){case "default":case "finally":return true}return false;case 10:return str === "instanceof";}
+},
+  6: function anonymous(str) {
+switch(str.length){case 5:switch(str){case "break":case "catch":case "throw":case "while":case "false":case "const":case "class":case "yield":case "super":return true}return false;case 4:switch(str){case "case":case "else":case "with":case "null":case "true":case "void":case "this":return true}return false;case 6:switch(str){case "return":case "switch":case "typeof":case "delete":case "export":case "import":return true}return false;case 3:switch(str){case "for":case "try":case "var":case "new":case "let":return true}return false;case 8:switch(str){case "continue":case "debugger":case "function":return true}return false;case 7:switch(str){case "default":case "finally":case "extends":return true}return false;case 2:switch(str){case "do":case "if":case "in":return true}return false;case 10:return str === "instanceof";}
+}
+};
+
+exports.keywords = keywords;
+// ## Character categories
+
+// Big ugly regular expressions that match characters in the
+// whitespace, identifier, and identifier-start categories. These
+// are only applied when a character is found to actually have a
+// code point above 128.
+// Generated by `tools/generate-identifier-regex.js`.
+
+var nonASCIIidentifierStartChars = "ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևא-תװ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠ-ࢲऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖ゛-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞭꞰꞱꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭟꭤꭥꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ﬀ-ﬆﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼＡ-Ｚａ-ｚｦ-ﾾￂ-ￇￊ-ￏￒ-ￗￚ-ￜ";
+var nonASCIIidentifierChars = "‌‍·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-٩ٰۖ-ۜ۟-۪ۤۧۨ-ۭ۰-۹ܑܰ-݊ަ-ް߀-߉߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛ࣤ-ःऺ-़ा-ॏ॑-ॗॢॣ०-९ঁ-ঃ়া-ৄেৈো-্ৗৢৣ০-৯ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣ૦-૯ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఀ-ఃా-ౄె-ైొ-్ౕౖౢౣ౦-౯ಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣ೦-೯ഁ-ഃാ-ൄെ-ൈൊ-്ൗൢൣ൦-൯ංඃ්ා-ුූෘ-ෟ෦-෯ෲෳัิ-ฺ็-๎๐-๙ັິ-ູົຼ່-ໍ໐-໙༘༙༠-༩༹༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှ၀-၉ၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟፩-፱ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝០-៩᠋-᠍᠐-᠙ᢩᤠ-ᤫᤰ-᤻᥆-᥏ᦰ-ᧀᧈᧉ᧐-᧚ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼-᪉᪐-᪙᪰-᪽ᬀ-ᬄ᬴-᭄᭐-᭙᭫-᭳ᮀ-ᮂᮡ-ᮭ᮰-᮹᯦-᯳ᰤ-᰷᱀-᱉᱐-᱙᳐-᳔᳒-᳨᳭ᳲ-᳴᳸᳹᷀-᷵᷼-᷿‿⁀⁔⃐-⃥⃜⃡-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꘠-꘩꙯ꙴ-꙽ꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-꣄꣐-꣙꣠-꣱꤀-꤉ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀꧐-꧙ꧥ꧰-꧹ꨩ-ꨶꩃꩌꩍ꩐-꩙ꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭꯰-꯹ﬞ︀-️︠-︭︳︴﹍-﹏０-９＿";
+
+var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
+var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
+
+nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
+
+// These are a run-length and offset encoded representation of the
+// >0xffff code points that are a valid part of identifiers. The
+// offset starts at 0x10000, and each pair of numbers represents an
+// offset to the next range, and then a size of the range. They were
+// generated by tools/generate-identifier-regex.js
+var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 17, 26, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 99, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 98, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 26, 45, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 955, 52, 76, 44, 33, 24, 27, 35, 42, 34, 4, 0, 13, 47, 15, 3, 22, 0, 38, 17, 2, 24, 133, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 32, 4, 287, 47, 21, 1, 2, 0, 185, 46, 82, 47, 21, 0, 60, 42, 502, 63, 32, 0, 449, 56, 1288, 920, 104, 110, 2962, 1070, 13266, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 881, 68, 12, 0, 67, 12, 16481, 1, 3071, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 4149, 196, 1340, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42710, 42, 4148, 12, 221, 16355, 541];
+var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 1306, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 52, 0, 13, 2, 49, 13, 16, 9, 83, 11, 168, 11, 6, 9, 8, 2, 57, 0, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 316, 19, 13, 9, 214, 6, 3, 8, 112, 16, 16, 9, 82, 12, 9, 9, 535, 9, 20855, 9, 135, 4, 60, 6, 26, 9, 1016, 45, 17, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 4305, 6, 792618, 239];
+
+// This has a complexity linear to the value of the code. The
+// assumption is that looking up astral identifier characters is
+// rare.
+function isInAstralSet(code, set) {
+  var pos = 0x10000;
+  for (var i = 0; i < set.length; i += 2) {
+    pos += set[i];
+    if (pos > code) return false;
+    pos += set[i + 1];
+    if (pos >= code) return true;
+  }
+}
+
+// Test whether a given character code starts an identifier.
+
+function isIdentifierStart(code, astral) {
+  if (code < 65) return code === 36;
+  if (code < 91) return true;
+  if (code < 97) return code === 95;
+  if (code < 123) return true;
+  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
+  if (astral === false) return false;
+  return isInAstralSet(code, astralIdentifierStartCodes);
+}
+
+// Test whether a given character is part of an identifier.
+
+function isIdentifierChar(code, astral) {
+  if (code < 48) return code === 36;
+  if (code < 58) return true;
+  if (code < 65) return false;
+  if (code < 91) return true;
+  if (code < 97) return code === 95;
+  if (code < 123) return true;
+  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
+  if (astral === false) return false;
+  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
+}
+
+},{}],3:[function(_dereq_,module,exports){
+// Acorn is a tiny, fast JavaScript parser written in JavaScript.
+//
+// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
+// various contributors and released under an MIT license.
+//
+// Git repositories for Acorn are available at
+//
+//     http://marijnhaverbeke.nl/git/acorn
+//     https://github.com/marijnh/acorn.git
+//
+// Please use the [github bug tracker][ghbt] to report issues.
+//
+// [ghbt]: https://github.com/marijnh/acorn/issues
+//
+// This file defines the main parser interface. The library also comes
+// with a [error-tolerant parser][dammit] and an
+// [abstract syntax tree walker][walk], defined in other files.
+//
+// [dammit]: acorn_loose.js
+// [walk]: util/walk.js
+
+"use strict";
+
+exports.__esModule = true;
+exports.parse = parse;
+exports.parseExpressionAt = parseExpressionAt;
+exports.tokenizer = tokenizer;
+
+var _state = _dereq_("./state");
+
+var _options = _dereq_("./options");
+
+_dereq_("./parseutil");
+
+_dereq_("./statement");
+
+_dereq_("./lval");
+
+_dereq_("./expression");
+
+_dereq_("./location");
+
+exports.Parser = _state.Parser;
+exports.plugins = _state.plugins;
+exports.defaultOptions = _options.defaultOptions;
+
+var _locutil = _dereq_("./locutil");
+
+exports.Position = _locutil.Position;
+exports.SourceLocation = _locutil.SourceLocation;
+exports.getLineInfo = _locutil.getLineInfo;
+
+var _node = _dereq_("./node");
+
+exports.Node = _node.Node;
+
+var _tokentype = _dereq_("./tokentype");
+
+exports.TokenType = _tokentype.TokenType;
+exports.tokTypes = _tokentype.types;
+
+var _tokencontext = _dereq_("./tokencontext");
+
+exports.TokContext = _tokencontext.TokContext;
+exports.tokContexts = _tokencontext.types;
+
+var _identifier = _dereq_("./identifier");
+
+exports.isIdentifierChar = _identifier.isIdentifierChar;
+exports.isIdentifierStart = _identifier.isIdentifierStart;
+
+var _tokenize = _dereq_("./tokenize");
+
+exports.Token = _tokenize.Token;
+
+var _whitespace = _dereq_("./whitespace");
+
+exports.isNewLine = _whitespace.isNewLine;
+exports.lineBreak = _whitespace.lineBreak;
+exports.lineBreakG = _whitespace.lineBreakG;
+var version = "2.4.0";
+
+exports.version = version;
+// The main exported interface (under `self.acorn` when in the
+// browser) is a `parse` function that takes a code string and
+// returns an abstract syntax tree as specified by [Mozilla parser
+// API][api].
+//
+// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+
+function parse(input, options) {
+  return new _state.Parser(options, input).parse();
+}
+
+// This function tries to parse a single expression at a given
+// offset in a string. Useful for parsing mixed-language formats
+// that embed JavaScript expressions.
+
+function parseExpressionAt(input, pos, options) {
+  var p = new _state.Parser(options, input, pos);
+  p.nextToken();
+  return p.parseExpression();
+}
+
+// Acorn is organized as a tokenizer and a recursive-descent parser.
+// The `tokenize` export provides an interface to the tokenizer.
+
+function tokenizer(input, options) {
+  return new _state.Parser(options, input);
+}
+
+},{"./expression":1,"./identifier":2,"./location":4,"./locutil":5,"./lval":6,"./node":7,"./options":8,"./parseutil":9,"./state":10,"./statement":11,"./tokencontext":12,"./tokenize":13,"./tokentype":14,"./whitespace":16}],4:[function(_dereq_,module,exports){
+"use strict";
+
+var _state = _dereq_("./state");
+
+var _locutil = _dereq_("./locutil");
+
+var pp = _state.Parser.prototype;
+
+// This function is used to raise exceptions on parse errors. It
+// takes an offset integer (into the current `input`) to indicate
+// the location of the error, attaches the position to the end
+// of the error message, and then raises a `SyntaxError` with that
+// message.
+
+pp.raise = function (pos, message) {
+  var loc = _locutil.getLineInfo(this.input, pos);
+  message += " (" + loc.line + ":" + loc.column + ")";
+  var err = new SyntaxError(message);
+  err.pos = pos;err.loc = loc;err.raisedAt = this.pos;
+  throw err;
+};
+
+pp.curPosition = function () {
+  if (this.options.locations) {
+    return new _locutil.Position(this.curLine, this.pos - this.lineStart);
+  }
+};
+
+},{"./locutil":5,"./state":10}],5:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.getLineInfo = getLineInfo;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _whitespace = _dereq_("./whitespace");
+
+// These are used when `options.locations` is on, for the
+// `startLoc` and `endLoc` properties.
+
+var Position = (function () {
+  function Position(line, col) {
+    _classCallCheck(this, Position);
+
+    this.line = line;
+    this.column = col;
+  }
+
+  Position.prototype.offset = function offset(n) {
+    return new Position(this.line, this.column + n);
+  };
+
+  return Position;
+})();
+
+exports.Position = Position;
+
+var SourceLocation = function SourceLocation(p, start, end) {
+  _classCallCheck(this, SourceLocation);
+
+  this.start = start;
+  this.end = end;
+  if (p.sourceFile !== null) this.source = p.sourceFile;
+};
+
+exports.SourceLocation = SourceLocation;
+
+// The `getLineInfo` function is mostly useful when the
+// `locations` option is off (for performance reasons) and you
+// want to find the line/column position for a given character
+// offset. `input` should be the code string that the offset refers
+// into.
+
+function getLineInfo(input, offset) {
+  for (var line = 1, cur = 0;;) {
+    _whitespace.lineBreakG.lastIndex = cur;
+    var match = _whitespace.lineBreakG.exec(input);
+    if (match && match.index < offset) {
+      ++line;
+      cur = match.index + match[0].length;
+    } else {
+      return new Position(line, offset - cur);
+    }
+  }
+}
+
+},{"./whitespace":16}],6:[function(_dereq_,module,exports){
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _identifier = _dereq_("./identifier");
+
+var _util = _dereq_("./util");
+
+var pp = _state.Parser.prototype;
+
+// Convert existing expression atom to assignable pattern
+// if possible.
+
+pp.toAssignable = function (node, isBinding) {
+  if (this.options.ecmaVersion >= 6 && node) {
+    switch (node.type) {
+      case "Identifier":
+      case "ObjectPattern":
+      case "ArrayPattern":
+      case "AssignmentPattern":
+        break;
+
+      case "ObjectExpression":
+        node.type = "ObjectPattern";
+        for (var i = 0; i < node.properties.length; i++) {
+          var prop = node.properties[i];
+          if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter");
+          this.toAssignable(prop.value, isBinding);
+        }
+        break;
+
+      case "ArrayExpression":
+        node.type = "ArrayPattern";
+        this.toAssignableList(node.elements, isBinding);
+        break;
+
+      case "AssignmentExpression":
+        if (node.operator === "=") {
+          node.type = "AssignmentPattern";
+          delete node.operator;
+        } else {
+          this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
+        }
+        break;
+
+      case "ParenthesizedExpression":
+        node.expression = this.toAssignable(node.expression, isBinding);
+        break;
+
+      case "MemberExpression":
+        if (!isBinding) break;
+
+      default:
+        this.raise(node.start, "Assigning to rvalue");
+    }
+  }
+  return node;
+};
+
+// Convert list of expression atoms to binding list.
+
+pp.toAssignableList = function (exprList, isBinding) {
+  var end = exprList.length;
+  if (end) {
+    var last = exprList[end - 1];
+    if (last && last.type == "RestElement") {
+      --end;
+    } else if (last && last.type == "SpreadElement") {
+      last.type = "RestElement";
+      var arg = last.argument;
+      this.toAssignable(arg, isBinding);
+      if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") this.unexpected(arg.start);
+      --end;
+    }
+  }
+  for (var i = 0; i < end; i++) {
+    var elt = exprList[i];
+    if (elt) this.toAssignable(elt, isBinding);
+  }
+  return exprList;
+};
+
+// Parses spread element.
+
+pp.parseSpread = function (refShorthandDefaultPos) {
+  var node = this.startNode();
+  this.next();
+  node.argument = this.parseMaybeAssign(refShorthandDefaultPos);
+  return this.finishNode(node, "SpreadElement");
+};
+
+pp.parseRest = function () {
+  var node = this.startNode();
+  this.next();
+  node.argument = this.type === _tokentype.types.name || this.type === _tokentype.types.bracketL ? this.parseBindingAtom() : this.unexpected();
+  return this.finishNode(node, "RestElement");
+};
+
+// Parses lvalue (assignable) atom.
+
+pp.parseBindingAtom = function () {
+  if (this.options.ecmaVersion < 6) return this.parseIdent();
+  switch (this.type) {
+    case _tokentype.types.name:
+      return this.parseIdent();
+
+    case _tokentype.types.bracketL:
+      var node = this.startNode();
+      this.next();
+      node.elements = this.parseBindingList(_tokentype.types.bracketR, true, true);
+      return this.finishNode(node, "ArrayPattern");
+
+    case _tokentype.types.braceL:
+      return this.parseObj(true);
+
+    default:
+      this.unexpected();
+  }
+};
+
+pp.parseBindingList = function (close, allowEmpty, allowTrailingComma) {
+  var elts = [],
+      first = true;
+  while (!this.eat(close)) {
+    if (first) first = false;else this.expect(_tokentype.types.comma);
+    if (allowEmpty && this.type === _tokentype.types.comma) {
+      elts.push(null);
+    } else if (allowTrailingComma && this.afterTrailingComma(close)) {
+      break;
+    } else if (this.type === _tokentype.types.ellipsis) {
+      var rest = this.parseRest();
+      this.parseBindingListItem(rest);
+      elts.push(rest);
+      this.expect(close);
+      break;
+    } else {
+      var elem = this.parseMaybeDefault(this.start, this.startLoc);
+      this.parseBindingListItem(elem);
+      elts.push(elem);
+    }
+  }
+  return elts;
+};
+
+pp.parseBindingListItem = function (param) {
+  return param;
+};
+
+// Parses assignment pattern around given atom if possible.
+
+pp.parseMaybeDefault = function (startPos, startLoc, left) {
+  left = left || this.parseBindingAtom();
+  if (this.options.ecmaVersion < 6 || !this.eat(_tokentype.types.eq)) return left;
+  var node = this.startNodeAt(startPos, startLoc);
+  node.left = left;
+  node.right = this.parseMaybeAssign();
+  return this.finishNode(node, "AssignmentPattern");
+};
+
+// Verify that a node is an lval — something that can be assigned
+// to.
+
+pp.checkLVal = function (expr, isBinding, checkClashes) {
+  switch (expr.type) {
+    case "Identifier":
+      if (this.strict && (_identifier.reservedWords.strictBind(expr.name) || _identifier.reservedWords.strict(expr.name))) this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode");
+      if (checkClashes) {
+        if (_util.has(checkClashes, expr.name)) this.raise(expr.start, "Argument name clash in strict mode");
+        checkClashes[expr.name] = true;
+      }
+      break;
+
+    case "MemberExpression":
+      if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
+      break;
+
+    case "ObjectPattern":
+      for (var i = 0; i < expr.properties.length; i++) {
+        this.checkLVal(expr.properties[i].value, isBinding, checkClashes);
+      }break;
+
+    case "ArrayPattern":
+      for (var i = 0; i < expr.elements.length; i++) {
+        var elem = expr.elements[i];
+        if (elem) this.checkLVal(elem, isBinding, checkClashes);
+      }
+      break;
+
+    case "AssignmentPattern":
+      this.checkLVal(expr.left, isBinding, checkClashes);
+      break;
+
+    case "RestElement":
+      this.checkLVal(expr.argument, isBinding, checkClashes);
+      break;
+
+    case "ParenthesizedExpression":
+      this.checkLVal(expr.expression, isBinding, checkClashes);
+      break;
+
+    default:
+      this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue");
+  }
+};
+
+},{"./identifier":2,"./state":10,"./tokentype":14,"./util":15}],7:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _state = _dereq_("./state");
+
+var _locutil = _dereq_("./locutil");
+
+var Node = function Node(parser, pos, loc) {
+  _classCallCheck(this, Node);
+
+  this.type = "";
+  this.start = pos;
+  this.end = 0;
+  if (parser.options.locations) this.loc = new _locutil.SourceLocation(parser, loc);
+  if (parser.options.directSourceFile) this.sourceFile = parser.options.directSourceFile;
+  if (parser.options.ranges) this.range = [pos, 0];
+};
+
+exports.Node = Node;
+
+// Start an AST node, attaching a start offset.
+
+var pp = _state.Parser.prototype;
+
+pp.startNode = function () {
+  return new Node(this, this.start, this.startLoc);
+};
+
+pp.startNodeAt = function (pos, loc) {
+  return new Node(this, pos, loc);
+};
+
+// Finish an AST node, adding `type` and `end` properties.
+
+function finishNodeAt(node, type, pos, loc) {
+  node.type = type;
+  node.end = pos;
+  if (this.options.locations) node.loc.end = loc;
+  if (this.options.ranges) node.range[1] = pos;
+  return node;
+}
+
+pp.finishNode = function (node, type) {
+  return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc);
+};
+
+// Finish node at given position
+
+pp.finishNodeAt = function (node, type, pos, loc) {
+  return finishNodeAt.call(this, node, type, pos, loc);
+};
+
+},{"./locutil":5,"./state":10}],8:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.getOptions = getOptions;
+
+var _util = _dereq_("./util");
+
+var _locutil = _dereq_("./locutil");
+
+// A second optional argument can be given to further configure
+// the parser process. These options are recognized:
+
+var defaultOptions = {
+  // `ecmaVersion` indicates the ECMAScript version to parse. Must
+  // be either 3, or 5, or 6. This influences support for strict
+  // mode, the set of reserved words, support for getters and
+  // setters and other features.
+  ecmaVersion: 5,
+  // Source type ("script" or "module") for different semantics
+  sourceType: "script",
+  // `onInsertedSemicolon` can be a callback that will be called
+  // when a semicolon is automatically inserted. It will be passed
+  // th position of the comma as an offset, and if `locations` is
+  // enabled, it is given the location as a `{line, column}` object
+  // as second argument.
+  onInsertedSemicolon: null,
+  // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
+  // trailing commas.
+  onTrailingComma: null,
+  // By default, reserved words are not enforced. Disable
+  // `allowReserved` to enforce them. When this option has the
+  // value "never", reserved words and keywords can also not be
+  // used as property names.
+  allowReserved: true,
+  // When enabled, a return at the top level is not considered an
+  // error.
+  allowReturnOutsideFunction: false,
+  // When enabled, import/export statements are not constrained to
+  // appearing at the top of the program.
+  allowImportExportEverywhere: false,
+  // When enabled, hashbang directive in the beginning of file
+  // is allowed and treated as a line comment.
+  allowHashBang: false,
+  // When `locations` is on, `loc` properties holding objects with
+  // `start` and `end` properties in `{line, column}` form (with
+  // line being 1-based and column 0-based) will be attached to the
+  // nodes.
+  locations: false,
+  // A function can be passed as `onToken` option, which will
+  // cause Acorn to call that function with object in the same
+  // format as tokenize() returns. Note that you are not
+  // allowed to call the parser from the callback—that will
+  // corrupt its internal state.
+  onToken: null,
+  // A function can be passed as `onComment` option, which will
+  // cause Acorn to call that function with `(block, text, start,
+  // end)` parameters whenever a comment is skipped. `block` is a
+  // boolean indicating whether this is a block (`/* */`) comment,
+  // `text` is the content of the comment, and `start` and `end` are
+  // character offsets that denote the start and end of the comment.
+  // When the `locations` option is on, two more parameters are
+  // passed, the full `{line, column}` locations of the start and
+  // end of the comments. Note that you are not allowed to call the
+  // parser from the callback—that will corrupt its internal state.
+  onComment: null,
+  // Nodes have their start and end characters offsets recorded in
+  // `start` and `end` properties (directly on the node, rather than
+  // the `loc` object, which holds line/column data. To also add a
+  // [semi-standardized][range] `range` property holding a `[start,
+  // end]` array with the same numbers, set the `ranges` option to
+  // `true`.
+  //
+  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
+  ranges: false,
+  // It is possible to parse multiple files into a single AST by
+  // passing the tree produced by parsing the first file as
+  // `program` option in subsequent parses. This will add the
+  // toplevel forms of the parsed file to the `Program` (top) node
+  // of an existing parse tree.
+  program: null,
+  // When `locations` is on, you can pass this to record the source
+  // file in every node's `loc` object.
+  sourceFile: null,
+  // This value, if given, is stored in every node, whether
+  // `locations` is on or off.
+  directSourceFile: null,
+  // When enabled, parenthesized expressions are represented by
+  // (non-standard) ParenthesizedExpression nodes
+  preserveParens: false,
+  plugins: {}
+};
+
+exports.defaultOptions = defaultOptions;
+// Interpret and default an options object
+
+function getOptions(opts) {
+  var options = {};
+  for (var opt in defaultOptions) {
+    options[opt] = opts && _util.has(opts, opt) ? opts[opt] : defaultOptions[opt];
+  }if (_util.isArray(options.onToken)) {
+    (function () {
+      var tokens = options.onToken;
+      options.onToken = function (token) {
+        return tokens.push(token);
+      };
+    })();
+  }
+  if (_util.isArray(options.onComment)) options.onComment = pushComment(options, options.onComment);
+
+  return options;
+}
+
+function pushComment(options, array) {
+  return function (block, text, start, end, startLoc, endLoc) {
+    var comment = {
+      type: block ? "Block" : "Line",
+      value: text,
+      start: start,
+      end: end
+    };
+    if (options.locations) comment.loc = new _locutil.SourceLocation(this, startLoc, endLoc);
+    if (options.ranges) comment.range = [start, end];
+    array.push(comment);
+  };
+}
+
+},{"./locutil":5,"./util":15}],9:[function(_dereq_,module,exports){
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _whitespace = _dereq_("./whitespace");
+
+var pp = _state.Parser.prototype;
+
+// ## Parser utilities
+
+// Test whether a statement node is the string literal `"use strict"`.
+
+pp.isUseStrict = function (stmt) {
+  return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" && stmt.expression.type === "Literal" && stmt.expression.raw.slice(1, -1) === "use strict";
+};
+
+// Predicate that tests whether the next token is of the given
+// type, and if yes, consumes it as a side effect.
+
+pp.eat = function (type) {
+  if (this.type === type) {
+    this.next();
+    return true;
+  } else {
+    return false;
+  }
+};
+
+// Tests whether parsed token is a contextual keyword.
+
+pp.isContextual = function (name) {
+  return this.type === _tokentype.types.name && this.value === name;
+};
+
+// Consumes contextual keyword if possible.
+
+pp.eatContextual = function (name) {
+  return this.value === name && this.eat(_tokentype.types.name);
+};
+
+// Asserts that following token is given contextual keyword.
+
+pp.expectContextual = function (name) {
+  if (!this.eatContextual(name)) this.unexpected();
+};
+
+// Test whether a semicolon can be inserted at the current position.
+
+pp.canInsertSemicolon = function () {
+  return this.type === _tokentype.types.eof || this.type === _tokentype.types.braceR || _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
+};
+
+pp.insertSemicolon = function () {
+  if (this.canInsertSemicolon()) {
+    if (this.options.onInsertedSemicolon) this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc);
+    return true;
+  }
+};
+
+// Consume a semicolon, or, failing that, see if we are allowed to
+// pretend that there is a semicolon at this position.
+
+pp.semicolon = function () {
+  if (!this.eat(_tokentype.types.semi) && !this.insertSemicolon()) this.unexpected();
+};
+
+pp.afterTrailingComma = function (tokType) {
+  if (this.type == tokType) {
+    if (this.options.onTrailingComma) this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
+    this.next();
+    return true;
+  }
+};
+
+// Expect a token of a given type. If found, consume it, otherwise,
+// raise an unexpected token error.
+
+pp.expect = function (type) {
+  this.eat(type) || this.unexpected();
+};
+
+// Raise an unexpected token error.
+
+pp.unexpected = function (pos) {
+  this.raise(pos != null ? pos : this.start, "Unexpected token");
+};
+
+},{"./state":10,"./tokentype":14,"./whitespace":16}],10:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _identifier = _dereq_("./identifier");
+
+var _tokentype = _dereq_("./tokentype");
+
+var _whitespace = _dereq_("./whitespace");
+
+var _options = _dereq_("./options");
+
+// Registered plugins
+var plugins = {};
+
+exports.plugins = plugins;
+
+var Parser = (function () {
+  function Parser(options, input, startPos) {
+    _classCallCheck(this, Parser);
+
+    this.options = _options.getOptions(options);
+    this.sourceFile = this.options.sourceFile;
+    this.isKeyword = _identifier.keywords[this.options.ecmaVersion >= 6 ? 6 : 5];
+    this.isReservedWord = _identifier.reservedWords[this.options.ecmaVersion];
+    this.input = String(input);
+
+    // Used to signal to callers of `readWord1` whether the word
+    // contained any escape sequences. This is needed because words with
+    // escape sequences must not be interpreted as keywords.
+    this.containsEsc = false;
+
+    // Load plugins
+    this.loadPlugins(this.options.plugins);
+
+    // Set up token state
+
+    // The current position of the tokenizer in the input.
+    if (startPos) {
+      this.pos = startPos;
+      this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos));
+      this.curLine = this.input.slice(0, this.lineStart).split(_whitespace.lineBreak).length;
+    } else {
+      this.pos = this.lineStart = 0;
+      this.curLine = 1;
+    }
+
+    // Properties of the current token:
+    // Its type
+    this.type = _tokentype.types.eof;
+    // For tokens that include more information than their type, the value
+    this.value = null;
+    // Its start and end offset
+    this.start = this.end = this.pos;
+    // And, if locations are used, the {line, column} object
+    // corresponding to those offsets
+    this.startLoc = this.endLoc = this.curPosition();
+
+    // Position information for the previous token
+    this.lastTokEndLoc = this.lastTokStartLoc = null;
+    this.lastTokStart = this.lastTokEnd = this.pos;
+
+    // The context stack is used to superficially track syntactic
+    // context to predict whether a regular expression is allowed in a
+    // given position.
+    this.context = this.initialContext();
+    this.exprAllowed = true;
+
+    // Figure out if it's a module code.
+    this.strict = this.inModule = this.options.sourceType === "module";
+
+    // Used to signify the start of a potential arrow function
+    this.potentialArrowAt = -1;
+
+    // Flags to track whether we are in a function, a generator.
+    this.inFunction = this.inGenerator = false;
+    // Labels in scope.
+    this.labels = [];
+
+    // If enabled, skip leading hashbang line.
+    if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === "#!") this.skipLineComment(2);
+  }
+
+  Parser.prototype.extend = function extend(name, f) {
+    this[name] = f(this[name]);
+  };
+
+  Parser.prototype.loadPlugins = function loadPlugins(pluginConfigs) {
+    for (var _name in pluginConfigs) {
+      var plugin = plugins[_name];
+      if (!plugin) throw new Error("Plugin '" + _name + "' not found");
+      plugin(this, pluginConfigs[_name]);
+    }
+  };
+
+  Parser.prototype.parse = function parse() {
+    var node = this.options.program || this.startNode();
+    this.nextToken();
+    return this.parseTopLevel(node);
+  };
+
+  return Parser;
+})();
+
+exports.Parser = Parser;
+
+},{"./identifier":2,"./options":8,"./tokentype":14,"./whitespace":16}],11:[function(_dereq_,module,exports){
+"use strict";
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _whitespace = _dereq_("./whitespace");
+
+var pp = _state.Parser.prototype;
+
+// ### Statement parsing
+
+// Parse a program. Initializes the parser, reads any number of
+// statements, and wraps them in a Program node.  Optionally takes a
+// `program` argument.  If present, the statements will be appended
+// to its body instead of creating a new node.
+
+pp.parseTopLevel = function (node) {
+  var first = true;
+  if (!node.body) node.body = [];
+  while (this.type !== _tokentype.types.eof) {
+    var stmt = this.parseStatement(true, true);
+    node.body.push(stmt);
+    if (first) {
+      if (this.isUseStrict(stmt)) this.setStrict(true);
+      first = false;
+    }
+  }
+  this.next();
+  if (this.options.ecmaVersion >= 6) {
+    node.sourceType = this.options.sourceType;
+  }
+  return this.finishNode(node, "Program");
+};
+
+var loopLabel = { kind: "loop" },
+    switchLabel = { kind: "switch" };
+
+// Parse a single statement.
+//
+// If expecting a statement and finding a slash operator, parse a
+// regular expression literal. This is to handle cases like
+// `if (foo) /blah/.exec(foo)`, where looking at the previous token
+// does not help.
+
+pp.parseStatement = function (declaration, topLevel) {
+  var starttype = this.type,
+      node = this.startNode();
+
+  // Most types of statements are recognized by the keyword they
+  // start with. Many are trivial to parse, some require a bit of
+  // complexity.
+
+  switch (starttype) {
+    case _tokentype.types._break:case _tokentype.types._continue:
+      return this.parseBreakContinueStatement(node, starttype.keyword);
+    case _tokentype.types._debugger:
+      return this.parseDebuggerStatement(node);
+    case _tokentype.types._do:
+      return this.parseDoStatement(node);
+    case _tokentype.types._for:
+      return this.parseForStatement(node);
+    case _tokentype.types._function:
+      if (!declaration && this.options.ecmaVersion >= 6) this.unexpected();
+      return this.parseFunctionStatement(node);
+    case _tokentype.types._class:
+      if (!declaration) this.unexpected();
+      return this.parseClass(node, true);
+    case _tokentype.types._if:
+      return this.parseIfStatement(node);
+    case _tokentype.types._return:
+      return this.parseReturnStatement(node);
+    case _tokentype.types._switch:
+      return this.parseSwitchStatement(node);
+    case _tokentype.types._throw:
+      return this.parseThrowStatement(node);
+    case _tokentype.types._try:
+      return this.parseTryStatement(node);
+    case _tokentype.types._let:case _tokentype.types._const:
+      if (!declaration) this.unexpected(); // NOTE: falls through to _var
+    case _tokentype.types._var:
+      return this.parseVarStatement(node, starttype);
+    case _tokentype.types._while:
+      return this.parseWhileStatement(node);
+    case _tokentype.types._with:
+      return this.parseWithStatement(node);
+    case _tokentype.types.braceL:
+      return this.parseBlock();
+    case _tokentype.types.semi:
+      return this.parseEmptyStatement(node);
+    case _tokentype.types._export:
+    case _tokentype.types._import:
+      if (!this.options.allowImportExportEverywhere) {
+        if (!topLevel) this.raise(this.start, "'import' and 'export' may only appear at the top level");
+        if (!this.inModule) this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'");
+      }
+      return starttype === _tokentype.types._import ? this.parseImport(node) : this.parseExport(node);
+
+    // If the statement does not start with a statement keyword or a
+    // brace, it's an ExpressionStatement or LabeledStatement. We
+    // simply start parsing an expression, and afterwards, if the
+    // next token is a colon and the expression was a simple
+    // Identifier node, we switch to interpreting it as a label.
+    default:
+      var maybeName = this.value,
+          expr = this.parseExpression();
+      if (starttype === _tokentype.types.name && expr.type === "Identifier" && this.eat(_tokentype.types.colon)) return this.parseLabeledStatement(node, maybeName, expr);else return this.parseExpressionStatement(node, expr);
+  }
+};
+
+pp.parseBreakContinueStatement = function (node, keyword) {
+  var isBreak = keyword == "break";
+  this.next();
+  if (this.eat(_tokentype.types.semi) || this.insertSemicolon()) node.label = null;else if (this.type !== _tokentype.types.name) this.unexpected();else {
+    node.label = this.parseIdent();
+    this.semicolon();
+  }
+
+  // Verify that there is an actual destination to break or
+  // continue to.
+  for (var i = 0; i < this.labels.length; ++i) {
+    var lab = this.labels[i];
+    if (node.label == null || lab.name === node.label.name) {
+      if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
+      if (node.label && isBreak) break;
+    }
+  }
+  if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword);
+  return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+};
+
+pp.parseDebuggerStatement = function (node) {
+  this.next();
+  this.semicolon();
+  return this.finishNode(node, "DebuggerStatement");
+};
+
+pp.parseDoStatement = function (node) {
+  this.next();
+  this.labels.push(loopLabel);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  this.expect(_tokentype.types._while);
+  node.test = this.parseParenExpression();
+  if (this.options.ecmaVersion >= 6) this.eat(_tokentype.types.semi);else this.semicolon();
+  return this.finishNode(node, "DoWhileStatement");
+};
+
+// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
+// loop is non-trivial. Basically, we have to parse the init `var`
+// statement or expression, disallowing the `in` operator (see
+// the second parameter to `parseExpression`), and then check
+// whether the next token is `in` or `of`. When there is no init
+// part (semicolon immediately after the opening parenthesis), it
+// is a regular `for` loop.
+
+pp.parseForStatement = function (node) {
+  this.next();
+  this.labels.push(loopLabel);
+  this.expect(_tokentype.types.parenL);
+  if (this.type === _tokentype.types.semi) return this.parseFor(node, null);
+  if (this.type === _tokentype.types._var || this.type === _tokentype.types._let || this.type === _tokentype.types._const) {
+    var _init = this.startNode(),
+        varKind = this.type;
+    this.next();
+    this.parseVar(_init, true, varKind);
+    this.finishNode(_init, "VariableDeclaration");
+    if ((this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) && _init.declarations.length === 1 && !(varKind !== _tokentype.types._var && _init.declarations[0].init)) return this.parseForIn(node, _init);
+    return this.parseFor(node, _init);
+  }
+  var refShorthandDefaultPos = { start: 0 };
+  var init = this.parseExpression(true, refShorthandDefaultPos);
+  if (this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of")) {
+    this.toAssignable(init);
+    this.checkLVal(init);
+    return this.parseForIn(node, init);
+  } else if (refShorthandDefaultPos.start) {
+    this.unexpected(refShorthandDefaultPos.start);
+  }
+  return this.parseFor(node, init);
+};
+
+pp.parseFunctionStatement = function (node) {
+  this.next();
+  return this.parseFunction(node, true);
+};
+
+pp.parseIfStatement = function (node) {
+  this.next();
+  node.test = this.parseParenExpression();
+  node.consequent = this.parseStatement(false);
+  node.alternate = this.eat(_tokentype.types._else) ? this.parseStatement(false) : null;
+  return this.finishNode(node, "IfStatement");
+};
+
+pp.parseReturnStatement = function (node) {
+  if (!this.inFunction && !this.options.allowReturnOutsideFunction) this.raise(this.start, "'return' outside of function");
+  this.next();
+
+  // In `return` (and `break`/`continue`), the keywords with
+  // optional arguments, we eagerly look for a semicolon or the
+  // possibility to insert one.
+
+  if (this.eat(_tokentype.types.semi) || this.insertSemicolon()) node.argument = null;else {
+    node.argument = this.parseExpression();this.semicolon();
+  }
+  return this.finishNode(node, "ReturnStatement");
+};
+
+pp.parseSwitchStatement = function (node) {
+  this.next();
+  node.discriminant = this.parseParenExpression();
+  node.cases = [];
+  this.expect(_tokentype.types.braceL);
+  this.labels.push(switchLabel);
+
+  // Statements under must be grouped (by label) in SwitchCase
+  // nodes. `cur` is used to keep the node that we are currently
+  // adding statements to.
+
+  for (var cur, sawDefault = false; this.type != _tokentype.types.braceR;) {
+    if (this.type === _tokentype.types._case || this.type === _tokentype.types._default) {
+      var isCase = this.type === _tokentype.types._case;
+      if (cur) this.finishNode(cur, "SwitchCase");
+      node.cases.push(cur = this.startNode());
+      cur.consequent = [];
+      this.next();
+      if (isCase) {
+        cur.test = this.parseExpression();
+      } else {
+        if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses");
+        sawDefault = true;
+        cur.test = null;
+      }
+      this.expect(_tokentype.types.colon);
+    } else {
+      if (!cur) this.unexpected();
+      cur.consequent.push(this.parseStatement(true));
+    }
+  }
+  if (cur) this.finishNode(cur, "SwitchCase");
+  this.next(); // Closing brace
+  this.labels.pop();
+  return this.finishNode(node, "SwitchStatement");
+};
+
+pp.parseThrowStatement = function (node) {
+  this.next();
+  if (_whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) this.raise(this.lastTokEnd, "Illegal newline after throw");
+  node.argument = this.parseExpression();
+  this.semicolon();
+  return this.finishNode(node, "ThrowStatement");
+};
+
+// Reused empty array added for node fields that are always empty.
+
+var empty = [];
+
+pp.parseTryStatement = function (node) {
+  this.next();
+  node.block = this.parseBlock();
+  node.handler = null;
+  if (this.type === _tokentype.types._catch) {
+    var clause = this.startNode();
+    this.next();
+    this.expect(_tokentype.types.parenL);
+    clause.param = this.parseBindingAtom();
+    this.checkLVal(clause.param, true);
+    this.expect(_tokentype.types.parenR);
+    clause.guard = null;
+    clause.body = this.parseBlock();
+    node.handler = this.finishNode(clause, "CatchClause");
+  }
+  node.guardedHandlers = empty;
+  node.finalizer = this.eat(_tokentype.types._finally) ? this.parseBlock() : null;
+  if (!node.handler && !node.finalizer) this.raise(node.start, "Missing catch or finally clause");
+  return this.finishNode(node, "TryStatement");
+};
+
+pp.parseVarStatement = function (node, kind) {
+  this.next();
+  this.parseVar(node, false, kind);
+  this.semicolon();
+  return this.finishNode(node, "VariableDeclaration");
+};
+
+pp.parseWhileStatement = function (node) {
+  this.next();
+  node.test = this.parseParenExpression();
+  this.labels.push(loopLabel);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  return this.finishNode(node, "WhileStatement");
+};
+
+pp.parseWithStatement = function (node) {
+  if (this.strict) this.raise(this.start, "'with' in strict mode");
+  this.next();
+  node.object = this.parseParenExpression();
+  node.body = this.parseStatement(false);
+  return this.finishNode(node, "WithStatement");
+};
+
+pp.parseEmptyStatement = function (node) {
+  this.next();
+  return this.finishNode(node, "EmptyStatement");
+};
+
+pp.parseLabeledStatement = function (node, maybeName, expr) {
+  for (var i = 0; i < this.labels.length; ++i) {
+    if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared");
+  }var kind = this.type.isLoop ? "loop" : this.type === _tokentype.types._switch ? "switch" : null;
+  for (var i = this.labels.length - 1; i >= 0; i--) {
+    var label = this.labels[i];
+    if (label.statementStart == node.start) {
+      label.statementStart = this.start;
+      label.kind = kind;
+    } else break;
+  }
+  this.labels.push({ name: maybeName, kind: kind, statementStart: this.start });
+  node.body = this.parseStatement(true);
+  this.labels.pop();
+  node.label = expr;
+  return this.finishNode(node, "LabeledStatement");
+};
+
+pp.parseExpressionStatement = function (node, expr) {
+  node.expression = expr;
+  this.semicolon();
+  return this.finishNode(node, "ExpressionStatement");
+};
+
+// Parse a semicolon-enclosed block of statements, handling `"use
+// strict"` declarations when `allowStrict` is true (used for
+// function bodies).
+
+pp.parseBlock = function (allowStrict) {
+  var node = this.startNode(),
+      first = true,
+      oldStrict = undefined;
+  node.body = [];
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    var stmt = this.parseStatement(true);
+    node.body.push(stmt);
+    if (first && allowStrict && this.isUseStrict(stmt)) {
+      oldStrict = this.strict;
+      this.setStrict(this.strict = true);
+    }
+    first = false;
+  }
+  if (oldStrict === false) this.setStrict(false);
+  return this.finishNode(node, "BlockStatement");
+};
+
+// Parse a regular `for` loop. The disambiguation code in
+// `parseStatement` will already have parsed the init statement or
+// expression.
+
+pp.parseFor = function (node, init) {
+  node.init = init;
+  this.expect(_tokentype.types.semi);
+  node.test = this.type === _tokentype.types.semi ? null : this.parseExpression();
+  this.expect(_tokentype.types.semi);
+  node.update = this.type === _tokentype.types.parenR ? null : this.parseExpression();
+  this.expect(_tokentype.types.parenR);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  return this.finishNode(node, "ForStatement");
+};
+
+// Parse a `for`/`in` and `for`/`of` loop, which are almost
+// same from parser's perspective.
+
+pp.parseForIn = function (node, init) {
+  var type = this.type === _tokentype.types._in ? "ForInStatement" : "ForOfStatement";
+  this.next();
+  node.left = init;
+  node.right = this.parseExpression();
+  this.expect(_tokentype.types.parenR);
+  node.body = this.parseStatement(false);
+  this.labels.pop();
+  return this.finishNode(node, type);
+};
+
+// Parse a list of variable declarations.
+
+pp.parseVar = function (node, isFor, kind) {
+  node.declarations = [];
+  node.kind = kind.keyword;
+  for (;;) {
+    var decl = this.startNode();
+    this.parseVarId(decl);
+    if (this.eat(_tokentype.types.eq)) {
+      decl.init = this.parseMaybeAssign(isFor);
+    } else if (kind === _tokentype.types._const && !(this.type === _tokentype.types._in || this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
+      this.unexpected();
+    } else if (decl.id.type != "Identifier" && !(isFor && (this.type === _tokentype.types._in || this.isContextual("of")))) {
+      this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value");
+    } else {
+      decl.init = null;
+    }
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+    if (!this.eat(_tokentype.types.comma)) break;
+  }
+  return node;
+};
+
+pp.parseVarId = function (decl) {
+  decl.id = this.parseBindingAtom();
+  this.checkLVal(decl.id, true);
+};
+
+// Parse a function declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseFunction = function (node, isStatement, allowExpressionBody) {
+  this.initFunction(node);
+  if (this.options.ecmaVersion >= 6) node.generator = this.eat(_tokentype.types.star);
+  if (isStatement || this.type === _tokentype.types.name) node.id = this.parseIdent();
+  this.parseFunctionParams(node);
+  this.parseFunctionBody(node, allowExpressionBody);
+  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+};
+
+pp.parseFunctionParams = function (node) {
+  this.expect(_tokentype.types.parenL);
+  node.params = this.parseBindingList(_tokentype.types.parenR, false, false);
+};
+
+// Parse a class declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseClass = function (node, isStatement) {
+  this.next();
+  this.parseClassId(node, isStatement);
+  this.parseClassSuper(node);
+  var classBody = this.startNode();
+  var hadConstructor = false;
+  classBody.body = [];
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (this.eat(_tokentype.types.semi)) continue;
+    var method = this.startNode();
+    var isGenerator = this.eat(_tokentype.types.star);
+    var isMaybeStatic = this.type === _tokentype.types.name && this.value === "static";
+    this.parsePropertyName(method);
+    method["static"] = isMaybeStatic && this.type !== _tokentype.types.parenL;
+    if (method["static"]) {
+      if (isGenerator) this.unexpected();
+      isGenerator = this.eat(_tokentype.types.star);
+      this.parsePropertyName(method);
+    }
+    method.kind = "method";
+    var isGetSet = false;
+    if (!method.computed) {
+      var key = method.key;
+
+      if (!isGenerator && key.type === "Identifier" && this.type !== _tokentype.types.parenL && (key.name === "get" || key.name === "set")) {
+        isGetSet = true;
+        method.kind = key.name;
+        key = this.parsePropertyName(method);
+      }
+      if (!method["static"] && (key.type === "Identifier" && key.name === "constructor" || key.type === "Literal" && key.value === "constructor")) {
+        if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class");
+        if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier");
+        if (isGenerator) this.raise(key.start, "Constructor can't be a generator");
+        method.kind = "constructor";
+        hadConstructor = true;
+      }
+    }
+    this.parseClassMethod(classBody, method, isGenerator);
+    if (isGetSet) {
+      var paramCount = method.kind === "get" ? 0 : 1;
+      if (method.value.params.length !== paramCount) {
+        var start = method.value.start;
+        if (method.kind === "get") this.raise(start, "getter should have no params");else this.raise(start, "setter should have exactly one param");
+      }
+    }
+  }
+  node.body = this.finishNode(classBody, "ClassBody");
+  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
+};
+
+pp.parseClassMethod = function (classBody, method, isGenerator) {
+  method.value = this.parseMethod(isGenerator);
+  classBody.body.push(this.finishNode(method, "MethodDefinition"));
+};
+
+pp.parseClassId = function (node, isStatement) {
+  node.id = this.type === _tokentype.types.name ? this.parseIdent() : isStatement ? this.unexpected() : null;
+};
+
+pp.parseClassSuper = function (node) {
+  node.superClass = this.eat(_tokentype.types._extends) ? this.parseExprSubscripts() : null;
+};
+
+// Parses module export declaration.
+
+pp.parseExport = function (node) {
+  this.next();
+  // export * from '...'
+  if (this.eat(_tokentype.types.star)) {
+    this.expectContextual("from");
+    node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
+    this.semicolon();
+    return this.finishNode(node, "ExportAllDeclaration");
+  }
+  if (this.eat(_tokentype.types._default)) {
+    // export default ...
+    var expr = this.parseMaybeAssign();
+    var needsSemi = true;
+    if (expr.type == "FunctionExpression" || expr.type == "ClassExpression") {
+      needsSemi = false;
+      if (expr.id) {
+        expr.type = expr.type == "FunctionExpression" ? "FunctionDeclaration" : "ClassDeclaration";
+      }
+    }
+    node.declaration = expr;
+    if (needsSemi) this.semicolon();
+    return this.finishNode(node, "ExportDefaultDeclaration");
+  }
+  // export var|const|let|function|class ...
+  if (this.shouldParseExportStatement()) {
+    node.declaration = this.parseStatement(true);
+    node.specifiers = [];
+    node.source = null;
+  } else {
+    // export { x, y as z } [from '...']
+    node.declaration = null;
+    node.specifiers = this.parseExportSpecifiers();
+    if (this.eatContextual("from")) {
+      node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
+    } else {
+      node.source = null;
+    }
+    this.semicolon();
+  }
+  return this.finishNode(node, "ExportNamedDeclaration");
+};
+
+pp.shouldParseExportStatement = function () {
+  return this.type.keyword;
+};
+
+// Parses a comma-separated list of module exports.
+
+pp.parseExportSpecifiers = function () {
+  var nodes = [],
+      first = true;
+  // export { x, y as z } [from '...']
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
+    } else first = false;
+
+    var node = this.startNode();
+    node.local = this.parseIdent(this.type === _tokentype.types._default);
+    node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local;
+    nodes.push(this.finishNode(node, "ExportSpecifier"));
+  }
+  return nodes;
+};
+
+// Parses import declaration.
+
+pp.parseImport = function (node) {
+  this.next();
+  // import '...'
+  if (this.type === _tokentype.types.string) {
+    node.specifiers = empty;
+    node.source = this.parseExprAtom();
+  } else {
+    node.specifiers = this.parseImportSpecifiers();
+    this.expectContextual("from");
+    node.source = this.type === _tokentype.types.string ? this.parseExprAtom() : this.unexpected();
+  }
+  this.semicolon();
+  return this.finishNode(node, "ImportDeclaration");
+};
+
+// Parses a comma-separated list of module imports.
+
+pp.parseImportSpecifiers = function () {
+  var nodes = [],
+      first = true;
+  if (this.type === _tokentype.types.name) {
+    // import defaultObj, { x, y as z } from '...'
+    var node = this.startNode();
+    node.local = this.parseIdent();
+    this.checkLVal(node.local, true);
+    nodes.push(this.finishNode(node, "ImportDefaultSpecifier"));
+    if (!this.eat(_tokentype.types.comma)) return nodes;
+  }
+  if (this.type === _tokentype.types.star) {
+    var node = this.startNode();
+    this.next();
+    this.expectContextual("as");
+    node.local = this.parseIdent();
+    this.checkLVal(node.local, true);
+    nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"));
+    return nodes;
+  }
+  this.expect(_tokentype.types.braceL);
+  while (!this.eat(_tokentype.types.braceR)) {
+    if (!first) {
+      this.expect(_tokentype.types.comma);
+      if (this.afterTrailingComma(_tokentype.types.braceR)) break;
+    } else first = false;
+
+    var node = this.startNode();
+    node.imported = this.parseIdent(true);
+    node.local = this.eatContextual("as") ? this.parseIdent() : node.imported;
+    this.checkLVal(node.local, true);
+    nodes.push(this.finishNode(node, "ImportSpecifier"));
+  }
+  return nodes;
+};
+
+},{"./state":10,"./tokentype":14,"./whitespace":16}],12:[function(_dereq_,module,exports){
+// The algorithm used to determine whether a regexp can appear at a
+// given point in the program is loosely based on sweet.js' approach.
+// See https://github.com/mozilla/sweet.js/wiki/design
+
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _state = _dereq_("./state");
+
+var _tokentype = _dereq_("./tokentype");
+
+var _whitespace = _dereq_("./whitespace");
+
+var TokContext = function TokContext(token, isExpr, preserveSpace, override) {
+  _classCallCheck(this, TokContext);
+
+  this.token = token;
+  this.isExpr = !!isExpr;
+  this.preserveSpace = !!preserveSpace;
+  this.override = override;
+};
+
+exports.TokContext = TokContext;
+var types = {
+  b_stat: new TokContext("{", false),
+  b_expr: new TokContext("{", true),
+  b_tmpl: new TokContext("${", true),
+  p_stat: new TokContext("(", false),
+  p_expr: new TokContext("(", true),
+  q_tmpl: new TokContext("`", true, true, function (p) {
+    return p.readTmplToken();
+  }),
+  f_expr: new TokContext("function", true)
+};
+
+exports.types = types;
+var pp = _state.Parser.prototype;
+
+pp.initialContext = function () {
+  return [types.b_stat];
+};
+
+pp.braceIsBlock = function (prevType) {
+  if (prevType === _tokentype.types.colon) {
+    var _parent = this.curContext();
+    if (_parent === types.b_stat || _parent === types.b_expr) return !_parent.isExpr;
+  }
+  if (prevType === _tokentype.types._return) return _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
+  if (prevType === _tokentype.types._else || prevType === _tokentype.types.semi || prevType === _tokentype.types.eof || prevType === _tokentype.types.parenR) return true;
+  if (prevType == _tokentype.types.braceL) return this.curContext() === types.b_stat;
+  return !this.exprAllowed;
+};
+
+pp.updateContext = function (prevType) {
+  var update = undefined,
+      type = this.type;
+  if (type.keyword && prevType == _tokentype.types.dot) this.exprAllowed = false;else if (update = type.updateContext) update.call(this, prevType);else this.exprAllowed = type.beforeExpr;
+};
+
+// Token-specific context update code
+
+_tokentype.types.parenR.updateContext = _tokentype.types.braceR.updateContext = function () {
+  if (this.context.length == 1) {
+    this.exprAllowed = true;
+    return;
+  }
+  var out = this.context.pop();
+  if (out === types.b_stat && this.curContext() === types.f_expr) {
+    this.context.pop();
+    this.exprAllowed = false;
+  } else if (out === types.b_tmpl) {
+    this.exprAllowed = true;
+  } else {
+    this.exprAllowed = !out.isExpr;
+  }
+};
+
+_tokentype.types.braceL.updateContext = function (prevType) {
+  this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);
+  this.exprAllowed = true;
+};
+
+_tokentype.types.dollarBraceL.updateContext = function () {
+  this.context.push(types.b_tmpl);
+  this.exprAllowed = true;
+};
+
+_tokentype.types.parenL.updateContext = function (prevType) {
+  var statementParens = prevType === _tokentype.types._if || prevType === _tokentype.types._for || prevType === _tokentype.types._with || prevType === _tokentype.types._while;
+  this.context.push(statementParens ? types.p_stat : types.p_expr);
+  this.exprAllowed = true;
+};
+
+_tokentype.types.incDec.updateContext = function () {};
+
+_tokentype.types._function.updateContext = function () {
+  if (this.curContext() !== types.b_stat) this.context.push(types.f_expr);
+  this.exprAllowed = false;
+};
+
+_tokentype.types.backQuote.updateContext = function () {
+  if (this.curContext() === types.q_tmpl) this.context.pop();else this.context.push(types.q_tmpl);
+  this.exprAllowed = false;
+};
+
+// tokExprAllowed stays unchanged
+
+},{"./state":10,"./tokentype":14,"./whitespace":16}],13:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _identifier = _dereq_("./identifier");
+
+var _tokentype = _dereq_("./tokentype");
+
+var _state = _dereq_("./state");
+
+var _locutil = _dereq_("./locutil");
+
+var _whitespace = _dereq_("./whitespace");
+
+// Object type used to represent tokens. Note that normally, tokens
+// simply exist as properties on the parser object. This is only
+// used for the onToken callback and the external tokenizer.
+
+var Token = function Token(p) {
+  _classCallCheck(this, Token);
+
+  this.type = p.type;
+  this.value = p.value;
+  this.start = p.start;
+  this.end = p.end;
+  if (p.options.locations) this.loc = new _locutil.SourceLocation(p, p.startLoc, p.endLoc);
+  if (p.options.ranges) this.range = [p.start, p.end];
+};
+
+exports.Token = Token;
+
+// ## Tokenizer
+
+var pp = _state.Parser.prototype;
+
+// Are we running under Rhino?
+var isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]";
+
+// Move to the next token
+
+pp.next = function () {
+  if (this.options.onToken) this.options.onToken(new Token(this));
+
+  this.lastTokEnd = this.end;
+  this.lastTokStart = this.start;
+  this.lastTokEndLoc = this.endLoc;
+  this.lastTokStartLoc = this.startLoc;
+  this.nextToken();
+};
+
+pp.getToken = function () {
+  this.next();
+  return new Token(this);
+};
+
+// If we're in an ES6 environment, make parsers iterable
+if (typeof Symbol !== "undefined") pp[Symbol.iterator] = function () {
+  var self = this;
+  return { next: function next() {
+      var token = self.getToken();
+      return {
+        done: token.type === _tokentype.types.eof,
+        value: token
+      };
+    } };
+};
+
+// Toggle strict mode. Re-reads the next number or string to please
+// pedantic tests (`"use strict"; 010;` should fail).
+
+pp.setStrict = function (strict) {
+  this.strict = strict;
+  if (this.type !== _tokentype.types.num && this.type !== _tokentype.types.string) return;
+  this.pos = this.start;
+  if (this.options.locations) {
+    while (this.pos < this.lineStart) {
+      this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
+      --this.curLine;
+    }
+  }
+  this.nextToken();
+};
+
+pp.curContext = function () {
+  return this.context[this.context.length - 1];
+};
+
+// Read a single token, updating the parser object's token-related
+// properties.
+
+pp.nextToken = function () {
+  var curContext = this.curContext();
+  if (!curContext || !curContext.preserveSpace) this.skipSpace();
+
+  this.start = this.pos;
+  if (this.options.locations) this.startLoc = this.curPosition();
+  if (this.pos >= this.input.length) return this.finishToken(_tokentype.types.eof);
+
+  if (curContext.override) return curContext.override(this);else this.readToken(this.fullCharCodeAtPos());
+};
+
+pp.readToken = function (code) {
+  // Identifier or keyword. '\uXXXX' sequences are allowed in
+  // identifiers, so '\' also dispatches to that.
+  if (_identifier.isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */) return this.readWord();
+
+  return this.getTokenFromCode(code);
+};
+
+pp.fullCharCodeAtPos = function () {
+  var code = this.input.charCodeAt(this.pos);
+  if (code <= 0xd7ff || code >= 0xe000) return code;
+  var next = this.input.charCodeAt(this.pos + 1);
+  return (code << 10) + next - 0x35fdc00;
+};
+
+pp.skipBlockComment = function () {
+  var startLoc = this.options.onComment && this.curPosition();
+  var start = this.pos,
+      end = this.input.indexOf("*/", this.pos += 2);
+  if (end === -1) this.raise(this.pos - 2, "Unterminated comment");
+  this.pos = end + 2;
+  if (this.options.locations) {
+    _whitespace.lineBreakG.lastIndex = start;
+    var match = undefined;
+    while ((match = _whitespace.lineBreakG.exec(this.input)) && match.index < this.pos) {
+      ++this.curLine;
+      this.lineStart = match.index + match[0].length;
+    }
+  }
+  if (this.options.onComment) this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos, startLoc, this.curPosition());
+};
+
+pp.skipLineComment = function (startSkip) {
+  var start = this.pos;
+  var startLoc = this.options.onComment && this.curPosition();
+  var ch = this.input.charCodeAt(this.pos += startSkip);
+  while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
+    ++this.pos;
+    ch = this.input.charCodeAt(this.pos);
+  }
+  if (this.options.onComment) this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos, startLoc, this.curPosition());
+};
+
+// Called at the start of the parse and after every token. Skips
+// whitespace and comments, and.
+
+pp.skipSpace = function () {
+  loop: while (this.pos < this.input.length) {
+    var ch = this.input.charCodeAt(this.pos);
+    switch (ch) {
+      case 32:case 160:
+        // ' '
+        ++this.pos;
+        break;
+      case 13:
+        if (this.input.charCodeAt(this.pos + 1) === 10) {
+          ++this.pos;
+        }
+      case 10:case 8232:case 8233:
+        ++this.pos;
+        if (this.options.locations) {
+          ++this.curLine;
+          this.lineStart = this.pos;
+        }
+        break;
+      case 47:
+        // '/'
+        switch (this.input.charCodeAt(this.pos + 1)) {
+          case 42:
+            // '*'
+            this.skipBlockComment();
+            break;
+          case 47:
+            this.skipLineComment(2);
+            break;
+          default:
+            break loop;
+        }
+        break;
+      default:
+        if (ch > 8 && ch < 14 || ch >= 5760 && _whitespace.nonASCIIwhitespace.test(String.fromCharCode(ch))) {
+          ++this.pos;
+        } else {
+          break loop;
+        }
+    }
+  }
+};
+
+// Called at the end of every token. Sets `end`, `val`, and
+// maintains `context` and `exprAllowed`, and skips the space after
+// the token, so that the next one's `start` will point at the
+// right position.
+
+pp.finishToken = function (type, val) {
+  this.end = this.pos;
+  if (this.options.locations) this.endLoc = this.curPosition();
+  var prevType = this.type;
+  this.type = type;
+  this.value = val;
+
+  this.updateContext(prevType);
+};
+
+// ### Token reading
+
+// This is the function that is called to fetch the next token. It
+// is somewhat obscure, because it works in character codes rather
+// than characters, and because operator parsing has been inlined
+// into it.
+//
+// All in the name of speed.
+//
+pp.readToken_dot = function () {
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next >= 48 && next <= 57) return this.readNumber(true);
+  var next2 = this.input.charCodeAt(this.pos + 2);
+  if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) {
+    // 46 = dot '.'
+    this.pos += 3;
+    return this.finishToken(_tokentype.types.ellipsis);
+  } else {
+    ++this.pos;
+    return this.finishToken(_tokentype.types.dot);
+  }
+};
+
+pp.readToken_slash = function () {
+  // '/'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (this.exprAllowed) {
+    ++this.pos;return this.readRegexp();
+  }
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(_tokentype.types.slash, 1);
+};
+
+pp.readToken_mult_modulo = function (code) {
+  // '%*'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(code === 42 ? _tokentype.types.star : _tokentype.types.modulo, 1);
+};
+
+pp.readToken_pipe_amp = function (code) {
+  // '|&'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === code) return this.finishOp(code === 124 ? _tokentype.types.logicalOR : _tokentype.types.logicalAND, 2);
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(code === 124 ? _tokentype.types.bitwiseOR : _tokentype.types.bitwiseAND, 1);
+};
+
+pp.readToken_caret = function () {
+  // '^'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(_tokentype.types.bitwiseXOR, 1);
+};
+
+pp.readToken_plus_min = function (code) {
+  // '+-'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === code) {
+    if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 && _whitespace.lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) {
+      // A `-->` line comment
+      this.skipLineComment(3);
+      this.skipSpace();
+      return this.nextToken();
+    }
+    return this.finishOp(_tokentype.types.incDec, 2);
+  }
+  if (next === 61) return this.finishOp(_tokentype.types.assign, 2);
+  return this.finishOp(_tokentype.types.plusMin, 1);
+};
+
+pp.readToken_lt_gt = function (code) {
+  // '<>'
+  var next = this.input.charCodeAt(this.pos + 1);
+  var size = 1;
+  if (next === code) {
+    size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
+    if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(_tokentype.types.assign, size + 1);
+    return this.finishOp(_tokentype.types.bitShift, size);
+  }
+  if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 && this.input.charCodeAt(this.pos + 3) == 45) {
+    if (this.inModule) this.unexpected();
+    // `<!--`, an XML-style comment that should be interpreted as a line comment
+    this.skipLineComment(4);
+    this.skipSpace();
+    return this.nextToken();
+  }
+  if (next === 61) size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2;
+  return this.finishOp(_tokentype.types.relational, size);
+};
+
+pp.readToken_eq_excl = function (code) {
+  // '=!'
+  var next = this.input.charCodeAt(this.pos + 1);
+  if (next === 61) return this.finishOp(_tokentype.types.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2);
+  if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) {
+    // '=>'
+    this.pos += 2;
+    return this.finishToken(_tokentype.types.arrow);
+  }
+  return this.finishOp(code === 61 ? _tokentype.types.eq : _tokentype.types.prefix, 1);
+};
+
+pp.getTokenFromCode = function (code) {
+  switch (code) {
+    // The interpretation of a dot depends on whether it is followed
+    // by a digit or another two dots.
+    case 46:
+      // '.'
+      return this.readToken_dot();
+
+    // Punctuation tokens.
+    case 40:
+      ++this.pos;return this.finishToken(_tokentype.types.parenL);
+    case 41:
+      ++this.pos;return this.finishToken(_tokentype.types.parenR);
+    case 59:
+      ++this.pos;return this.finishToken(_tokentype.types.semi);
+    case 44:
+      ++this.pos;return this.finishToken(_tokentype.types.comma);
+    case 91:
+      ++this.pos;return this.finishToken(_tokentype.types.bracketL);
+    case 93:
+      ++this.pos;return this.finishToken(_tokentype.types.bracketR);
+    case 123:
+      ++this.pos;return this.finishToken(_tokentype.types.braceL);
+    case 125:
+      ++this.pos;return this.finishToken(_tokentype.types.braceR);
+    case 58:
+      ++this.pos;return this.finishToken(_tokentype.types.colon);
+    case 63:
+      ++this.pos;return this.finishToken(_tokentype.types.question);
+
+    case 96:
+      // '`'
+      if (this.options.ecmaVersion < 6) break;
+      ++this.pos;
+      return this.finishToken(_tokentype.types.backQuote);
+
+    case 48:
+      // '0'
+      var next = this.input.charCodeAt(this.pos + 1);
+      if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
+      if (this.options.ecmaVersion >= 6) {
+        if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
+        if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
+      }
+    // Anything else beginning with a digit is an integer, octal
+    // number, or float.
+    case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:
+      // 1-9
+      return this.readNumber(false);
+
+    // Quotes produce strings.
+    case 34:case 39:
+      // '"', "'"
+      return this.readString(code);
+
+    // Operators are parsed inline in tiny state machines. '=' (61) is
+    // often referred to. `finishOp` simply skips the amount of
+    // characters it is given as second argument, and returns a token
+    // of the type given by its first argument.
+
+    case 47:
+      // '/'
+      return this.readToken_slash();
+
+    case 37:case 42:
+      // '%*'
+      return this.readToken_mult_modulo(code);
+
+    case 124:case 38:
+      // '|&'
+      return this.readToken_pipe_amp(code);
+
+    case 94:
+      // '^'
+      return this.readToken_caret();
+
+    case 43:case 45:
+      // '+-'
+      return this.readToken_plus_min(code);
+
+    case 60:case 62:
+      // '<>'
+      return this.readToken_lt_gt(code);
+
+    case 61:case 33:
+      // '=!'
+      return this.readToken_eq_excl(code);
+
+    case 126:
+      // '~'
+      return this.finishOp(_tokentype.types.prefix, 1);
+  }
+
+  this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
+};
+
+pp.finishOp = function (type, size) {
+  var str = this.input.slice(this.pos, this.pos + size);
+  this.pos += size;
+  return this.finishToken(type, str);
+};
+
+// Parse a regular expression. Some context-awareness is necessary,
+// since a '/' inside a '[]' set does not end the expression.
+
+function tryCreateRegexp(src, flags, throwErrorAt) {
+  try {
+    return new RegExp(src, flags);
+  } catch (e) {
+    if (throwErrorAt !== undefined) {
+      if (e instanceof SyntaxError) this.raise(throwErrorAt, "Error parsing regular expression: " + e.message);
+      this.raise(e);
+    }
+  }
+}
+
+var regexpUnicodeSupport = !!tryCreateRegexp("", "u");
+
+pp.readRegexp = function () {
+  var _this = this;
+
+  var escaped = undefined,
+      inClass = undefined,
+      start = this.pos;
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
+    var ch = this.input.charAt(this.pos);
+    if (_whitespace.lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
+    if (!escaped) {
+      if (ch === "[") inClass = true;else if (ch === "]" && inClass) inClass = false;else if (ch === "/" && !inClass) break;
+      escaped = ch === "\\";
+    } else escaped = false;
+    ++this.pos;
+  }
+  var content = this.input.slice(start, this.pos);
+  ++this.pos;
+  // Need to use `readWord1` because '\uXXXX' sequences are allowed
+  // here (don't ask).
+  var mods = this.readWord1();
+  var tmp = content;
+  if (mods) {
+    var validFlags = /^[gmsiy]*$/;
+    if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/;
+    if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag");
+    if (mods.indexOf("u") >= 0 && !regexpUnicodeSupport) {
+      // Replace each astral symbol and every Unicode escape sequence that
+      // possibly represents an astral symbol or a paired surrogate with a
+      // single ASCII symbol to avoid throwing on regular expressions that
+      // are only valid in combination with the `/u` flag.
+      // Note: replacing with the ASCII symbol `x` might cause false
+      // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
+      // perfectly valid pattern that is equivalent to `[a-b]`, but it would
+      // be replaced by `[x-b]` which throws an error.
+      tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, function (match, code, offset) {
+        code = Number("0x" + code);
+        if (code > 0x10FFFF) _this.raise(start + offset + 3, "Code point out of bounds");
+        return "x";
+      });
+      tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
+    }
+  }
+  // Detect invalid regular expressions.
+  var value = null;
+  // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
+  // so don't do detection if we are running under Rhino
+  if (!isRhino) {
+    tryCreateRegexp(tmp, undefined, start);
+    // Get a regular expression object for this pattern-flag pair, or `null` in
+    // case the current environment doesn't support the flags it uses.
+    value = tryCreateRegexp(content, mods);
+  }
+  return this.finishToken(_tokentype.types.regexp, { pattern: content, flags: mods, value: value });
+};
+
+// Read an integer in the given radix. Return null if zero digits
+// were read, the integer value otherwise. When `len` is given, this
+// will return `null` unless the integer has exactly `len` digits.
+
+pp.readInt = function (radix, len) {
+  var start = this.pos,
+      total = 0;
+  for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
+    var code = this.input.charCodeAt(this.pos),
+        val = undefined;
+    if (code >= 97) val = code - 97 + 10; // a
+    else if (code >= 65) val = code - 65 + 10; // A
+    else if (code >= 48 && code <= 57) val = code - 48; // 0-9
+    else val = Infinity;
+    if (val >= radix) break;
+    ++this.pos;
+    total = total * radix + val;
+  }
+  if (this.pos === start || len != null && this.pos - start !== len) return null;
+
+  return total;
+};
+
+pp.readRadixNumber = function (radix) {
+  this.pos += 2; // 0x
+  var val = this.readInt(radix);
+  if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix);
+  if (_identifier.isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
+  return this.finishToken(_tokentype.types.num, val);
+};
+
+// Read an integer, octal integer, or floating-point number.
+
+pp.readNumber = function (startsWithDot) {
+  var start = this.pos,
+      isFloat = false,
+      octal = this.input.charCodeAt(this.pos) === 48;
+  if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number");
+  var next = this.input.charCodeAt(this.pos);
+  if (next === 46) {
+    // '.'
+    ++this.pos;
+    this.readInt(10);
+    isFloat = true;
+    next = this.input.charCodeAt(this.pos);
+  }
+  if (next === 69 || next === 101) {
+    // 'eE'
+    next = this.input.charCodeAt(++this.pos);
+    if (next === 43 || next === 45) ++this.pos; // '+-'
+    if (this.readInt(10) === null) this.raise(start, "Invalid number");
+    isFloat = true;
+  }
+  if (_identifier.isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
+
+  var str = this.input.slice(start, this.pos),
+      val = undefined;
+  if (isFloat) val = parseFloat(str);else if (!octal || str.length === 1) val = parseInt(str, 10);else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number");else val = parseInt(str, 8);
+  return this.finishToken(_tokentype.types.num, val);
+};
+
+// Read a string value, interpreting backslash-escapes.
+
+pp.readCodePoint = function () {
+  var ch = this.input.charCodeAt(this.pos),
+      code = undefined;
+
+  if (ch === 123) {
+    if (this.options.ecmaVersion < 6) this.unexpected();
+    var codePos = ++this.pos;
+    code = this.readHexChar(this.input.indexOf("}", this.pos) - this.pos);
+    ++this.pos;
+    if (code > 0x10FFFF) this.raise(codePos, "Code point out of bounds");
+  } else {
+    code = this.readHexChar(4);
+  }
+  return code;
+};
+
+function codePointToString(code) {
+  // UTF-16 Decoding
+  if (code <= 0xFFFF) return String.fromCharCode(code);
+  code -= 0x10000;
+  return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00);
+}
+
+pp.readString = function (quote) {
+  var out = "",
+      chunkStart = ++this.pos;
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant");
+    var ch = this.input.charCodeAt(this.pos);
+    if (ch === quote) break;
+    if (ch === 92) {
+      // '\'
+      out += this.input.slice(chunkStart, this.pos);
+      out += this.readEscapedChar(false);
+      chunkStart = this.pos;
+    } else {
+      if (_whitespace.isNewLine(ch)) this.raise(this.start, "Unterminated string constant");
+      ++this.pos;
+    }
+  }
+  out += this.input.slice(chunkStart, this.pos++);
+  return this.finishToken(_tokentype.types.string, out);
+};
+
+// Reads template string tokens.
+
+pp.readTmplToken = function () {
+  var out = "",
+      chunkStart = this.pos;
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template");
+    var ch = this.input.charCodeAt(this.pos);
+    if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) {
+      // '`', '${'
+      if (this.pos === this.start && this.type === _tokentype.types.template) {
+        if (ch === 36) {
+          this.pos += 2;
+          return this.finishToken(_tokentype.types.dollarBraceL);
+        } else {
+          ++this.pos;
+          return this.finishToken(_tokentype.types.backQuote);
+        }
+      }
+      out += this.input.slice(chunkStart, this.pos);
+      return this.finishToken(_tokentype.types.template, out);
+    }
+    if (ch === 92) {
+      // '\'
+      out += this.input.slice(chunkStart, this.pos);
+      out += this.readEscapedChar(true);
+      chunkStart = this.pos;
+    } else if (_whitespace.isNewLine(ch)) {
+      out += this.input.slice(chunkStart, this.pos);
+      ++this.pos;
+      switch (ch) {
+        case 13:
+          if (this.input.charCodeAt(this.pos) === 10) ++this.pos;
+        case 10:
+          out += "\n";
+          break;
+        default:
+          out += String.fromCharCode(ch);
+          break;
+      }
+      if (this.options.locations) {
+        ++this.curLine;
+        this.lineStart = this.pos;
+      }
+      chunkStart = this.pos;
+    } else {
+      ++this.pos;
+    }
+  }
+};
+
+// Used to read escaped characters
+
+pp.readEscapedChar = function (inTemplate) {
+  var ch = this.input.charCodeAt(++this.pos);
+  ++this.pos;
+  switch (ch) {
+    case 110:
+      return "\n"; // 'n' -> '\n'
+    case 114:
+      return "\r"; // 'r' -> '\r'
+    case 120:
+      return String.fromCharCode(this.readHexChar(2)); // 'x'
+    case 117:
+      return codePointToString(this.readCodePoint()); // 'u'
+    case 116:
+      return "\t"; // 't' -> '\t'
+    case 98:
+      return "\b"; // 'b' -> '\b'
+    case 118:
+      return "\u000b"; // 'v' -> '\u000b'
+    case 102:
+      return "\f"; // 'f' -> '\f'
+    case 13:
+      if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
+    case 10:
+      // ' \n'
+      if (this.options.locations) {
+        this.lineStart = this.pos;++this.curLine;
+      }
+      return "";
+    default:
+      if (ch >= 48 && ch <= 55) {
+        var octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0];
+        var octal = parseInt(octalStr, 8);
+        if (octal > 255) {
+          octalStr = octalStr.slice(0, -1);
+          octal = parseInt(octalStr, 8);
+        }
+        if (octal > 0 && (this.strict || inTemplate)) {
+          this.raise(this.pos - 2, "Octal literal in strict mode");
+        }
+        this.pos += octalStr.length - 1;
+        return String.fromCharCode(octal);
+      }
+      return String.fromCharCode(ch);
+  }
+};
+
+// Used to read character escape sequences ('\x', '\u', '\U').
+
+pp.readHexChar = function (len) {
+  var codePos = this.pos;
+  var n = this.readInt(16, len);
+  if (n === null) this.raise(codePos, "Bad character escape sequence");
+  return n;
+};
+
+// Read an identifier, and return it as a string. Sets `this.containsEsc`
+// to whether the word contained a '\u' escape.
+//
+// Incrementally adds only escaped chars, adding other chunks as-is
+// as a micro-optimization.
+
+pp.readWord1 = function () {
+  this.containsEsc = false;
+  var word = "",
+      first = true,
+      chunkStart = this.pos;
+  var astral = this.options.ecmaVersion >= 6;
+  while (this.pos < this.input.length) {
+    var ch = this.fullCharCodeAtPos();
+    if (_identifier.isIdentifierChar(ch, astral)) {
+      this.pos += ch <= 0xffff ? 1 : 2;
+    } else if (ch === 92) {
+      // "\"
+      this.containsEsc = true;
+      word += this.input.slice(chunkStart, this.pos);
+      var escStart = this.pos;
+      if (this.input.charCodeAt(++this.pos) != 117) // "u"
+        this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX");
+      ++this.pos;
+      var esc = this.readCodePoint();
+      if (!(first ? _identifier.isIdentifierStart : _identifier.isIdentifierChar)(esc, astral)) this.raise(escStart, "Invalid Unicode escape");
+      word += codePointToString(esc);
+      chunkStart = this.pos;
+    } else {
+      break;
+    }
+    first = false;
+  }
+  return word + this.input.slice(chunkStart, this.pos);
+};
+
+// Read an identifier or keyword token. Will check for reserved
+// words when necessary.
+
+pp.readWord = function () {
+  var word = this.readWord1();
+  var type = _tokentype.types.name;
+  if ((this.options.ecmaVersion >= 6 || !this.containsEsc) && this.isKeyword(word)) type = _tokentype.keywords[word];
+  return this.finishToken(type, word);
+};
+
+},{"./identifier":2,"./locutil":5,"./state":10,"./tokentype":14,"./whitespace":16}],14:[function(_dereq_,module,exports){
+// ## Token types
+
+// The assignment of fine-grained, information-carrying type objects
+// allows the tokenizer to store the information it has about a
+// token in a way that is very cheap for the parser to look up.
+
+// All token type variables start with an underscore, to make them
+// easy to recognize.
+
+// The `beforeExpr` property is used to disambiguate between regular
+// expressions and divisions. It is set on all token types that can
+// be followed by an expression (thus, a slash after them would be a
+// regular expression).
+//
+// `isLoop` marks a keyword as starting a loop, which is important
+// to know when parsing a label, in order to allow or disallow
+// continue jumps to that label.
+
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var TokenType = function TokenType(label) {
+  var conf = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+  _classCallCheck(this, TokenType);
+
+  this.label = label;
+  this.keyword = conf.keyword;
+  this.beforeExpr = !!conf.beforeExpr;
+  this.startsExpr = !!conf.startsExpr;
+  this.isLoop = !!conf.isLoop;
+  this.isAssign = !!conf.isAssign;
+  this.prefix = !!conf.prefix;
+  this.postfix = !!conf.postfix;
+  this.binop = conf.binop || null;
+  this.updateContext = null;
+};
+
+exports.TokenType = TokenType;
+
+function binop(name, prec) {
+  return new TokenType(name, { beforeExpr: true, binop: prec });
+}
+var beforeExpr = { beforeExpr: true },
+    startsExpr = { startsExpr: true };
+
+var types = {
+  num: new TokenType("num", startsExpr),
+  regexp: new TokenType("regexp", startsExpr),
+  string: new TokenType("string", startsExpr),
+  name: new TokenType("name", startsExpr),
+  eof: new TokenType("eof"),
+
+  // Punctuation token types.
+  bracketL: new TokenType("[", { beforeExpr: true, startsExpr: true }),
+  bracketR: new TokenType("]"),
+  braceL: new TokenType("{", { beforeExpr: true, startsExpr: true }),
+  braceR: new TokenType("}"),
+  parenL: new TokenType("(", { beforeExpr: true, startsExpr: true }),
+  parenR: new TokenType(")"),
+  comma: new TokenType(",", beforeExpr),
+  semi: new TokenType(";", beforeExpr),
+  colon: new TokenType(":", beforeExpr),
+  dot: new TokenType("."),
+  question: new TokenType("?", beforeExpr),
+  arrow: new TokenType("=>", beforeExpr),
+  template: new TokenType("template"),
+  ellipsis: new TokenType("...", beforeExpr),
+  backQuote: new TokenType("`", startsExpr),
+  dollarBraceL: new TokenType("${", { beforeExpr: true, startsExpr: true }),
+
+  // Operators. These carry several kinds of properties to help the
+  // parser use them properly (the presence of these properties is
+  // what categorizes them as operators).
+  //
+  // `binop`, when present, specifies that this operator is a binary
+  // operator, and will refer to its precedence.
+  //
+  // `prefix` and `postfix` mark the operator as a prefix or postfix
+  // unary operator.
+  //
+  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
+  // binary operators with a very low precedence, that should result
+  // in AssignmentExpression nodes.
+
+  eq: new TokenType("=", { beforeExpr: true, isAssign: true }),
+  assign: new TokenType("_=", { beforeExpr: true, isAssign: true }),
+  incDec: new TokenType("++/--", { prefix: true, postfix: true, startsExpr: true }),
+  prefix: new TokenType("prefix", { beforeExpr: true, prefix: true, startsExpr: true }),
+  logicalOR: binop("||", 1),
+  logicalAND: binop("&&", 2),
+  bitwiseOR: binop("|", 3),
+  bitwiseXOR: binop("^", 4),
+  bitwiseAND: binop("&", 5),
+  equality: binop("==/!=", 6),
+  relational: binop("</>", 7),
+  bitShift: binop("<</>>", 8),
+  plusMin: new TokenType("+/-", { beforeExpr: true, binop: 9, prefix: true, startsExpr: true }),
+  modulo: binop("%", 10),
+  star: binop("*", 10),
+  slash: binop("/", 10)
+};
+
+exports.types = types;
+// Map keyword names to token types.
+
+var keywords = {};
+
+exports.keywords = keywords;
+// Succinct definitions of keyword token types
+function kw(name) {
+  var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+  options.keyword = name;
+  keywords[name] = types["_" + name] = new TokenType(name, options);
+}
+
+kw("break");
+kw("case", beforeExpr);
+kw("catch");
+kw("continue");
+kw("debugger");
+kw("default", beforeExpr);
+kw("do", { isLoop: true });
+kw("else", beforeExpr);
+kw("finally");
+kw("for", { isLoop: true });
+kw("function", startsExpr);
+kw("if");
+kw("return", beforeExpr);
+kw("switch");
+kw("throw", beforeExpr);
+kw("try");
+kw("var");
+kw("let");
+kw("const");
+kw("while", { isLoop: true });
+kw("with");
+kw("new", { beforeExpr: true, startsExpr: true });
+kw("this", startsExpr);
+kw("super", startsExpr);
+kw("class");
+kw("extends", beforeExpr);
+kw("export");
+kw("import");
+kw("yield", { beforeExpr: true, startsExpr: true });
+kw("null", startsExpr);
+kw("true", startsExpr);
+kw("false", startsExpr);
+kw("in", { beforeExpr: true, binop: 7 });
+kw("instanceof", { beforeExpr: true, binop: 7 });
+kw("typeof", { beforeExpr: true, prefix: true, startsExpr: true });
+kw("void", { beforeExpr: true, prefix: true, startsExpr: true });
+kw("delete", { beforeExpr: true, prefix: true, startsExpr: true });
+
+},{}],15:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.isArray = isArray;
+exports.has = has;
+
+function isArray(obj) {
+  return Object.prototype.toString.call(obj) === "[object Array]";
+}
+
+// Checks if an object has a property.
+
+function has(obj, propName) {
+  return Object.prototype.hasOwnProperty.call(obj, propName);
+}
+
+},{}],16:[function(_dereq_,module,exports){
+// Matches a whole line break (where CRLF is considered a single
+// line break). Used to count lines.
+
+"use strict";
+
+exports.__esModule = true;
+exports.isNewLine = isNewLine;
+var lineBreak = /\r\n?|\n|\u2028|\u2029/;
+exports.lineBreak = lineBreak;
+var lineBreakG = new RegExp(lineBreak.source, "g");
+
+exports.lineBreakG = lineBreakG;
+
+function isNewLine(code) {
+  return code === 10 || code === 13 || code === 0x2028 || code == 0x2029;
+}
+
+var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
+exports.nonASCIIwhitespace = nonASCIIwhitespace;
+
+},{}]},{},[3])(3)
+});
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn_loose.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn_loose.js
new file mode 100644
index 0000000..9185349
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/acorn_loose.js
@@ -0,0 +1,1279 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.acorn || (g.acorn = {})).loose = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+"use strict";
+
+module.exports = typeof acorn != 'undefined' ? acorn : require("./acorn");
+
+},{}],2:[function(_dereq_,module,exports){
+"use strict";
+
+var _state = _dereq_("./state");
+
+var _parseutil = _dereq_("./parseutil");
+
+var _ = _dereq_("..");
+
+var lp = _state.LooseParser.prototype;
+
+lp.checkLVal = function (expr, binding) {
+  if (!expr) return expr;
+  switch (expr.type) {
+    case "Identifier":
+      return expr;
+
+    case "MemberExpression":
+      return binding ? this.dummyIdent() : expr;
+
+    case "ParenthesizedExpression":
+      expr.expression = this.checkLVal(expr.expression, binding);
+      return expr;
+
+    // FIXME recursively check contents
+    case "ObjectPattern":
+    case "ArrayPattern":
+    case "RestElement":
+    case "AssignmentPattern":
+      if (this.options.ecmaVersion >= 6) return expr;
+
+    default:
+      return this.dummyIdent();
+  }
+};
+
+lp.parseExpression = function (noIn) {
+  var start = this.storeCurrentPos();
+  var expr = this.parseMaybeAssign(noIn);
+  if (this.tok.type === _.tokTypes.comma) {
+    var node = this.startNodeAt(start);
+    node.expressions = [expr];
+    while (this.eat(_.tokTypes.comma)) node.expressions.push(this.parseMaybeAssign(noIn));
+    return this.finishNode(node, "SequenceExpression");
+  }
+  return expr;
+};
+
+lp.parseParenExpression = function () {
+  this.pushCx();
+  this.expect(_.tokTypes.parenL);
+  var val = this.parseExpression();
+  this.popCx();
+  this.expect(_.tokTypes.parenR);
+  return val;
+};
+
+lp.parseMaybeAssign = function (noIn) {
+  var start = this.storeCurrentPos();
+  var left = this.parseMaybeConditional(noIn);
+  if (this.tok.type.isAssign) {
+    var node = this.startNodeAt(start);
+    node.operator = this.tok.value;
+    node.left = this.tok.type === _.tokTypes.eq ? this.toAssignable(left) : this.checkLVal(left);
+    this.next();
+    node.right = this.parseMaybeAssign(noIn);
+    return this.finishNode(node, "AssignmentExpression");
+  }
+  return left;
+};
+
+lp.parseMaybeConditional = function (noIn) {
+  var start = this.storeCurrentPos();
+  var expr = this.parseExprOps(noIn);
+  if (this.eat(_.tokTypes.question)) {
+    var node = this.startNodeAt(start);
+    node.test = expr;
+    node.consequent = this.parseMaybeAssign();
+    node.alternate = this.expect(_.tokTypes.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent();
+    return this.finishNode(node, "ConditionalExpression");
+  }
+  return expr;
+};
+
+lp.parseExprOps = function (noIn) {
+  var start = this.storeCurrentPos();
+  var indent = this.curIndent,
+      line = this.curLineStart;
+  return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line);
+};
+
+lp.parseExprOp = function (left, start, minPrec, noIn, indent, line) {
+  if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left;
+  var prec = this.tok.type.binop;
+  if (prec != null && (!noIn || this.tok.type !== _.tokTypes._in)) {
+    if (prec > minPrec) {
+      var node = this.startNodeAt(start);
+      node.left = left;
+      node.operator = this.tok.value;
+      this.next();
+      if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
+        node.right = this.dummyIdent();
+      } else {
+        var rightStart = this.storeCurrentPos();
+        node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line);
+      }
+      this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
+      return this.parseExprOp(node, start, minPrec, noIn, indent, line);
+    }
+  }
+  return left;
+};
+
+lp.parseMaybeUnary = function (noIn) {
+  if (this.tok.type.prefix) {
+    var node = this.startNode(),
+        update = this.tok.type === _.tokTypes.incDec;
+    node.operator = this.tok.value;
+    node.prefix = true;
+    this.next();
+    node.argument = this.parseMaybeUnary(noIn);
+    if (update) node.argument = this.checkLVal(node.argument);
+    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+  } else if (this.tok.type === _.tokTypes.ellipsis) {
+    var node = this.startNode();
+    this.next();
+    node.argument = this.parseMaybeUnary(noIn);
+    return this.finishNode(node, "SpreadElement");
+  }
+  var start = this.storeCurrentPos();
+  var expr = this.parseExprSubscripts();
+  while (this.tok.type.postfix && !this.canInsertSemicolon()) {
+    var node = this.startNodeAt(start);
+    node.operator = this.tok.value;
+    node.prefix = false;
+    node.argument = this.checkLVal(expr);
+    this.next();
+    expr = this.finishNode(node, "UpdateExpression");
+  }
+  return expr;
+};
+
+lp.parseExprSubscripts = function () {
+  var start = this.storeCurrentPos();
+  return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart);
+};
+
+lp.parseSubscripts = function (base, start, noCalls, startIndent, line) {
+  for (;;) {
+    if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) {
+      if (this.tok.type == _.tokTypes.dot && this.curIndent == startIndent) --startIndent;else return base;
+    }
+
+    if (this.eat(_.tokTypes.dot)) {
+      var node = this.startNodeAt(start);
+      node.object = base;
+      if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) node.property = this.dummyIdent();else node.property = this.parsePropertyAccessor() || this.dummyIdent();
+      node.computed = false;
+      base = this.finishNode(node, "MemberExpression");
+    } else if (this.tok.type == _.tokTypes.bracketL) {
+      this.pushCx();
+      this.next();
+      var node = this.startNodeAt(start);
+      node.object = base;
+      node.property = this.parseExpression();
+      node.computed = true;
+      this.popCx();
+      this.expect(_.tokTypes.bracketR);
+      base = this.finishNode(node, "MemberExpression");
+    } else if (!noCalls && this.tok.type == _.tokTypes.parenL) {
+      var node = this.startNodeAt(start);
+      node.callee = base;
+      node.arguments = this.parseExprList(_.tokTypes.parenR);
+      base = this.finishNode(node, "CallExpression");
+    } else if (this.tok.type == _.tokTypes.backQuote) {
+      var node = this.startNodeAt(start);
+      node.tag = base;
+      node.quasi = this.parseTemplate();
+      base = this.finishNode(node, "TaggedTemplateExpression");
+    } else {
+      return base;
+    }
+  }
+};
+
+lp.parseExprAtom = function () {
+  var node = undefined;
+  switch (this.tok.type) {
+    case _.tokTypes._this:
+    case _.tokTypes._super:
+      var type = this.tok.type === _.tokTypes._this ? "ThisExpression" : "Super";
+      node = this.startNode();
+      this.next();
+      return this.finishNode(node, type);
+
+    case _.tokTypes.name:
+      var start = this.storeCurrentPos();
+      var id = this.parseIdent();
+      return this.eat(_.tokTypes.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id;
+
+    case _.tokTypes.regexp:
+      node = this.startNode();
+      var val = this.tok.value;
+      node.regex = { pattern: val.pattern, flags: val.flags };
+      node.value = val.value;
+      node.raw = this.input.slice(this.tok.start, this.tok.end);
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case _.tokTypes.num:case _.tokTypes.string:
+      node = this.startNode();
+      node.value = this.tok.value;
+      node.raw = this.input.slice(this.tok.start, this.tok.end);
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case _.tokTypes._null:case _.tokTypes._true:case _.tokTypes._false:
+      node = this.startNode();
+      node.value = this.tok.type === _.tokTypes._null ? null : this.tok.type === _.tokTypes._true;
+      node.raw = this.tok.type.keyword;
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case _.tokTypes.parenL:
+      var parenStart = this.storeCurrentPos();
+      this.next();
+      var inner = this.parseExpression();
+      this.expect(_.tokTypes.parenR);
+      if (this.eat(_.tokTypes.arrow)) {
+        return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (_parseutil.isDummy(inner) ? [] : [inner]));
+      }
+      if (this.options.preserveParens) {
+        var par = this.startNodeAt(parenStart);
+        par.expression = inner;
+        inner = this.finishNode(par, "ParenthesizedExpression");
+      }
+      return inner;
+
+    case _.tokTypes.bracketL:
+      node = this.startNode();
+      node.elements = this.parseExprList(_.tokTypes.bracketR, true);
+      return this.finishNode(node, "ArrayExpression");
+
+    case _.tokTypes.braceL:
+      return this.parseObj();
+
+    case _.tokTypes._class:
+      return this.parseClass();
+
+    case _.tokTypes._function:
+      node = this.startNode();
+      this.next();
+      return this.parseFunction(node, false);
+
+    case _.tokTypes._new:
+      return this.parseNew();
+
+    case _.tokTypes._yield:
+      node = this.startNode();
+      this.next();
+      if (this.semicolon() || this.canInsertSemicolon() || this.tok.type != _.tokTypes.star && !this.tok.type.startsExpr) {
+        node.delegate = false;
+        node.argument = null;
+      } else {
+        node.delegate = this.eat(_.tokTypes.star);
+        node.argument = this.parseMaybeAssign();
+      }
+      return this.finishNode(node, "YieldExpression");
+
+    case _.tokTypes.backQuote:
+      return this.parseTemplate();
+
+    default:
+      return this.dummyIdent();
+  }
+};
+
+lp.parseNew = function () {
+  var node = this.startNode(),
+      startIndent = this.curIndent,
+      line = this.curLineStart;
+  var meta = this.parseIdent(true);
+  if (this.options.ecmaVersion >= 6 && this.eat(_.tokTypes.dot)) {
+    node.meta = meta;
+    node.property = this.parseIdent(true);
+    return this.finishNode(node, "MetaProperty");
+  }
+  var start = this.storeCurrentPos();
+  node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line);
+  if (this.tok.type == _.tokTypes.parenL) {
+    node.arguments = this.parseExprList(_.tokTypes.parenR);
+  } else {
+    node.arguments = [];
+  }
+  return this.finishNode(node, "NewExpression");
+};
+
+lp.parseTemplateElement = function () {
+  var elem = this.startNode();
+  elem.value = {
+    raw: this.input.slice(this.tok.start, this.tok.end).replace(/\r\n?/g, "\n"),
+    cooked: this.tok.value
+  };
+  this.next();
+  elem.tail = this.tok.type === _.tokTypes.backQuote;
+  return this.finishNode(elem, "TemplateElement");
+};
+
+lp.parseTemplate = function () {
+  var node = this.startNode();
+  this.next();
+  node.expressions = [];
+  var curElt = this.parseTemplateElement();
+  node.quasis = [curElt];
+  while (!curElt.tail) {
+    this.next();
+    node.expressions.push(this.parseExpression());
+    if (this.expect(_.tokTypes.braceR)) {
+      curElt = this.parseTemplateElement();
+    } else {
+      curElt = this.startNode();
+      curElt.value = { cooked: "", raw: "" };
+      curElt.tail = true;
+    }
+    node.quasis.push(curElt);
+  }
+  this.expect(_.tokTypes.backQuote);
+  return this.finishNode(node, "TemplateLiteral");
+};
+
+lp.parseObj = function () {
+  var node = this.startNode();
+  node.properties = [];
+  this.pushCx();
+  var indent = this.curIndent + 1,
+      line = this.curLineStart;
+  this.eat(_.tokTypes.braceL);
+  if (this.curIndent + 1 < indent) {
+    indent = this.curIndent;line = this.curLineStart;
+  }
+  while (!this.closes(_.tokTypes.braceR, indent, line)) {
+    var prop = this.startNode(),
+        isGenerator = undefined,
+        start = undefined;
+    if (this.options.ecmaVersion >= 6) {
+      start = this.storeCurrentPos();
+      prop.method = false;
+      prop.shorthand = false;
+      isGenerator = this.eat(_.tokTypes.star);
+    }
+    this.parsePropertyName(prop);
+    if (_parseutil.isDummy(prop.key)) {
+      if (_parseutil.isDummy(this.parseMaybeAssign())) this.next();this.eat(_.tokTypes.comma);continue;
+    }
+    if (this.eat(_.tokTypes.colon)) {
+      prop.kind = "init";
+      prop.value = this.parseMaybeAssign();
+    } else if (this.options.ecmaVersion >= 6 && (this.tok.type === _.tokTypes.parenL || this.tok.type === _.tokTypes.braceL)) {
+      prop.kind = "init";
+      prop.method = true;
+      prop.value = this.parseMethod(isGenerator);
+    } else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" && !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && (this.tok.type != _.tokTypes.comma && this.tok.type != _.tokTypes.braceR)) {
+      prop.kind = prop.key.name;
+      this.parsePropertyName(prop);
+      prop.value = this.parseMethod(false);
+    } else {
+      prop.kind = "init";
+      if (this.options.ecmaVersion >= 6) {
+        if (this.eat(_.tokTypes.eq)) {
+          var assign = this.startNodeAt(start);
+          assign.operator = "=";
+          assign.left = prop.key;
+          assign.right = this.parseMaybeAssign();
+          prop.value = this.finishNode(assign, "AssignmentExpression");
+        } else {
+          prop.value = prop.key;
+        }
+      } else {
+        prop.value = this.dummyIdent();
+      }
+      prop.shorthand = true;
+    }
+    node.properties.push(this.finishNode(prop, "Property"));
+    this.eat(_.tokTypes.comma);
+  }
+  this.popCx();
+  if (!this.eat(_.tokTypes.braceR)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start;
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
+  }
+  return this.finishNode(node, "ObjectExpression");
+};
+
+lp.parsePropertyName = function (prop) {
+  if (this.options.ecmaVersion >= 6) {
+    if (this.eat(_.tokTypes.bracketL)) {
+      prop.computed = true;
+      prop.key = this.parseExpression();
+      this.expect(_.tokTypes.bracketR);
+      return;
+    } else {
+      prop.computed = false;
+    }
+  }
+  var key = this.tok.type === _.tokTypes.num || this.tok.type === _.tokTypes.string ? this.parseExprAtom() : this.parseIdent();
+  prop.key = key || this.dummyIdent();
+};
+
+lp.parsePropertyAccessor = function () {
+  if (this.tok.type === _.tokTypes.name || this.tok.type.keyword) return this.parseIdent();
+};
+
+lp.parseIdent = function () {
+  var name = this.tok.type === _.tokTypes.name ? this.tok.value : this.tok.type.keyword;
+  if (!name) return this.dummyIdent();
+  var node = this.startNode();
+  this.next();
+  node.name = name;
+  return this.finishNode(node, "Identifier");
+};
+
+lp.initFunction = function (node) {
+  node.id = null;
+  node.params = [];
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = false;
+    node.expression = false;
+  }
+};
+
+// Convert existing expression atom to assignable pattern
+// if possible.
+
+lp.toAssignable = function (node, binding) {
+  if (this.options.ecmaVersion >= 6 && node) {
+    switch (node.type) {
+      case "ObjectExpression":
+        node.type = "ObjectPattern";
+        var props = node.properties;
+        for (var i = 0; i < props.length; i++) {
+          this.toAssignable(props[i].value, binding);
+        }break;
+
+      case "ArrayExpression":
+        node.type = "ArrayPattern";
+        this.toAssignableList(node.elements, binding);
+        break;
+
+      case "SpreadElement":
+        node.type = "RestElement";
+        node.argument = this.toAssignable(node.argument, binding);
+        break;
+
+      case "AssignmentExpression":
+        node.type = "AssignmentPattern";
+        delete node.operator;
+        break;
+    }
+  }
+  return this.checkLVal(node, binding);
+};
+
+lp.toAssignableList = function (exprList, binding) {
+  for (var i = 0; i < exprList.length; i++) {
+    exprList[i] = this.toAssignable(exprList[i], binding);
+  }return exprList;
+};
+
+lp.parseFunctionParams = function (params) {
+  params = this.parseExprList(_.tokTypes.parenR);
+  return this.toAssignableList(params, true);
+};
+
+lp.parseMethod = function (isGenerator) {
+  var node = this.startNode();
+  this.initFunction(node);
+  node.params = this.parseFunctionParams();
+  node.generator = isGenerator || false;
+  node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== _.tokTypes.braceL;
+  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
+  return this.finishNode(node, "FunctionExpression");
+};
+
+lp.parseArrowExpression = function (node, params) {
+  this.initFunction(node);
+  node.params = this.toAssignableList(params, true);
+  node.expression = this.tok.type !== _.tokTypes.braceL;
+  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
+  return this.finishNode(node, "ArrowFunctionExpression");
+};
+
+lp.parseExprList = function (close, allowEmpty) {
+  this.pushCx();
+  var indent = this.curIndent,
+      line = this.curLineStart,
+      elts = [];
+  this.next(); // Opening bracket
+  while (!this.closes(close, indent + 1, line)) {
+    if (this.eat(_.tokTypes.comma)) {
+      elts.push(allowEmpty ? null : this.dummyIdent());
+      continue;
+    }
+    var elt = this.parseMaybeAssign();
+    if (_parseutil.isDummy(elt)) {
+      if (this.closes(close, indent, line)) break;
+      this.next();
+    } else {
+      elts.push(elt);
+    }
+    this.eat(_.tokTypes.comma);
+  }
+  this.popCx();
+  if (!this.eat(close)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start;
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
+  }
+  return elts;
+};
+
+},{"..":1,"./parseutil":4,"./state":5}],3:[function(_dereq_,module,exports){
+// Acorn: Loose parser
+//
+// This module provides an alternative parser (`parse_dammit`) that
+// exposes that same interface as `parse`, but will try to parse
+// anything as JavaScript, repairing syntax error the best it can.
+// There are circumstances in which it will raise an error and give
+// up, but they are very rare. The resulting AST will be a mostly
+// valid JavaScript AST (as per the [Mozilla parser API][api], except
+// that:
+//
+// - Return outside functions is allowed
+//
+// - Label consistency (no conflicts, break only to existing labels)
+//   is not enforced.
+//
+// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
+//   the parser got too confused to return anything meaningful.
+//
+// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+//
+// The expected use for this is to *first* try `acorn.parse`, and only
+// if that fails switch to `parse_dammit`. The loose parser might
+// parse badly indented code incorrectly, so **don't** use it as
+// your default parser.
+//
+// Quite a lot of acorn.js is duplicated here. The alternative was to
+// add a *lot* of extra cruft to that file, making it less readable
+// and slower. Copying and editing the code allowed me to make
+// invasive changes and simplifications without creating a complicated
+// tangle.
+
+"use strict";
+
+exports.__esModule = true;
+exports.parse_dammit = parse_dammit;
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }
+
+var _ = _dereq_("..");
+
+var acorn = _interopRequireWildcard(_);
+
+var _state = _dereq_("./state");
+
+_dereq_("./tokenize");
+
+_dereq_("./statement");
+
+_dereq_("./expression");
+
+exports.LooseParser = _state.LooseParser;
+
+acorn.defaultOptions.tabSize = 4;
+
+function parse_dammit(input, options) {
+  var p = new _state.LooseParser(input, options);
+  p.next();
+  return p.parseTopLevel();
+}
+
+acorn.parse_dammit = parse_dammit;
+acorn.LooseParser = _state.LooseParser;
+
+},{"..":1,"./expression":2,"./state":5,"./statement":6,"./tokenize":7}],4:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+exports.isDummy = isDummy;
+
+function isDummy(node) {
+  return node.name == "✖";
+}
+
+},{}],5:[function(_dereq_,module,exports){
+"use strict";
+
+exports.__esModule = true;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var _ = _dereq_("..");
+
+var LooseParser = (function () {
+  function LooseParser(input, options) {
+    _classCallCheck(this, LooseParser);
+
+    this.toks = _.tokenizer(input, options);
+    this.options = this.toks.options;
+    this.input = this.toks.input;
+    this.tok = this.last = { type: _.tokTypes.eof, start: 0, end: 0 };
+    if (this.options.locations) {
+      var here = this.toks.curPosition();
+      this.tok.loc = new _.SourceLocation(this.toks, here, here);
+    }
+    this.ahead = []; // Tokens ahead
+    this.context = []; // Indentation contexted
+    this.curIndent = 0;
+    this.curLineStart = 0;
+    this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
+  }
+
+  LooseParser.prototype.startNode = function startNode() {
+    return new _.Node(this.toks, this.tok.start, this.options.locations ? this.tok.loc.start : null);
+  };
+
+  LooseParser.prototype.storeCurrentPos = function storeCurrentPos() {
+    return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start;
+  };
+
+  LooseParser.prototype.startNodeAt = function startNodeAt(pos) {
+    if (this.options.locations) {
+      return new _.Node(this.toks, pos[0], pos[1]);
+    } else {
+      return new _.Node(this.toks, pos);
+    }
+  };
+
+  LooseParser.prototype.finishNode = function finishNode(node, type) {
+    node.type = type;
+    node.end = this.last.end;
+    if (this.options.locations) node.loc.end = this.last.loc.end;
+    if (this.options.ranges) node.range[1] = this.last.end;
+    return node;
+  };
+
+  LooseParser.prototype.dummyIdent = function dummyIdent() {
+    var dummy = this.startNode();
+    dummy.name = "✖";
+    return this.finishNode(dummy, "Identifier");
+  };
+
+  LooseParser.prototype.dummyString = function dummyString() {
+    var dummy = this.startNode();
+    dummy.value = dummy.raw = "✖";
+    return this.finishNode(dummy, "Literal");
+  };
+
+  LooseParser.prototype.eat = function eat(type) {
+    if (this.tok.type === type) {
+      this.next();
+      return true;
+    } else {
+      return false;
+    }
+  };
+
+  LooseParser.prototype.isContextual = function isContextual(name) {
+    return this.tok.type === _.tokTypes.name && this.tok.value === name;
+  };
+
+  LooseParser.prototype.eatContextual = function eatContextual(name) {
+    return this.tok.value === name && this.eat(_.tokTypes.name);
+  };
+
+  LooseParser.prototype.canInsertSemicolon = function canInsertSemicolon() {
+    return this.tok.type === _.tokTypes.eof || this.tok.type === _.tokTypes.braceR || _.lineBreak.test(this.input.slice(this.last.end, this.tok.start));
+  };
+
+  LooseParser.prototype.semicolon = function semicolon() {
+    return this.eat(_.tokTypes.semi);
+  };
+
+  LooseParser.prototype.expect = function expect(type) {
+    if (this.eat(type)) return true;
+    for (var i = 1; i <= 2; i++) {
+      if (this.lookAhead(i).type == type) {
+        for (var j = 0; j < i; j++) {
+          this.next();
+        }return true;
+      }
+    }
+  };
+
+  LooseParser.prototype.pushCx = function pushCx() {
+    this.context.push(this.curIndent);
+  };
+
+  LooseParser.prototype.popCx = function popCx() {
+    this.curIndent = this.context.pop();
+  };
+
+  LooseParser.prototype.lineEnd = function lineEnd(pos) {
+    while (pos < this.input.length && !_.isNewLine(this.input.charCodeAt(pos))) ++pos;
+    return pos;
+  };
+
+  LooseParser.prototype.indentationAfter = function indentationAfter(pos) {
+    for (var count = 0;; ++pos) {
+      var ch = this.input.charCodeAt(pos);
+      if (ch === 32) ++count;else if (ch === 9) count += this.options.tabSize;else return count;
+    }
+  };
+
+  LooseParser.prototype.closes = function closes(closeTok, indent, line, blockHeuristic) {
+    if (this.tok.type === closeTok || this.tok.type === _.tokTypes.eof) return true;
+    return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() && (!blockHeuristic || this.nextLineStart >= this.input.length || this.indentationAfter(this.nextLineStart) < indent);
+  };
+
+  LooseParser.prototype.tokenStartsLine = function tokenStartsLine() {
+    for (var p = this.tok.start - 1; p >= this.curLineStart; --p) {
+      var ch = this.input.charCodeAt(p);
+      if (ch !== 9 && ch !== 32) return false;
+    }
+    return true;
+  };
+
+  return LooseParser;
+})();
+
+exports.LooseParser = LooseParser;
+
+},{"..":1}],6:[function(_dereq_,module,exports){
+"use strict";
+
+var _state = _dereq_("./state");
+
+var _parseutil = _dereq_("./parseutil");
+
+var _ = _dereq_("..");
+
+var lp = _state.LooseParser.prototype;
+
+lp.parseTopLevel = function () {
+  var node = this.startNodeAt(this.options.locations ? [0, _.getLineInfo(this.input, 0)] : 0);
+  node.body = [];
+  while (this.tok.type !== _.tokTypes.eof) node.body.push(this.parseStatement());
+  this.last = this.tok;
+  if (this.options.ecmaVersion >= 6) {
+    node.sourceType = this.options.sourceType;
+  }
+  return this.finishNode(node, "Program");
+};
+
+lp.parseStatement = function () {
+  var starttype = this.tok.type,
+      node = this.startNode();
+
+  switch (starttype) {
+    case _.tokTypes._break:case _.tokTypes._continue:
+      this.next();
+      var isBreak = starttype === _.tokTypes._break;
+      if (this.semicolon() || this.canInsertSemicolon()) {
+        node.label = null;
+      } else {
+        node.label = this.tok.type === _.tokTypes.name ? this.parseIdent() : null;
+        this.semicolon();
+      }
+      return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+
+    case _.tokTypes._debugger:
+      this.next();
+      this.semicolon();
+      return this.finishNode(node, "DebuggerStatement");
+
+    case _.tokTypes._do:
+      this.next();
+      node.body = this.parseStatement();
+      node.test = this.eat(_.tokTypes._while) ? this.parseParenExpression() : this.dummyIdent();
+      this.semicolon();
+      return this.finishNode(node, "DoWhileStatement");
+
+    case _.tokTypes._for:
+      this.next();
+      this.pushCx();
+      this.expect(_.tokTypes.parenL);
+      if (this.tok.type === _.tokTypes.semi) return this.parseFor(node, null);
+      if (this.tok.type === _.tokTypes._var || this.tok.type === _.tokTypes._let || this.tok.type === _.tokTypes._const) {
+        var _init = this.parseVar(true);
+        if (_init.declarations.length === 1 && (this.tok.type === _.tokTypes._in || this.isContextual("of"))) {
+          return this.parseForIn(node, _init);
+        }
+        return this.parseFor(node, _init);
+      }
+      var init = this.parseExpression(true);
+      if (this.tok.type === _.tokTypes._in || this.isContextual("of")) return this.parseForIn(node, this.toAssignable(init));
+      return this.parseFor(node, init);
+
+    case _.tokTypes._function:
+      this.next();
+      return this.parseFunction(node, true);
+
+    case _.tokTypes._if:
+      this.next();
+      node.test = this.parseParenExpression();
+      node.consequent = this.parseStatement();
+      node.alternate = this.eat(_.tokTypes._else) ? this.parseStatement() : null;
+      return this.finishNode(node, "IfStatement");
+
+    case _.tokTypes._return:
+      this.next();
+      if (this.eat(_.tokTypes.semi) || this.canInsertSemicolon()) node.argument = null;else {
+        node.argument = this.parseExpression();this.semicolon();
+      }
+      return this.finishNode(node, "ReturnStatement");
+
+    case _.tokTypes._switch:
+      var blockIndent = this.curIndent,
+          line = this.curLineStart;
+      this.next();
+      node.discriminant = this.parseParenExpression();
+      node.cases = [];
+      this.pushCx();
+      this.expect(_.tokTypes.braceL);
+
+      var cur = undefined;
+      while (!this.closes(_.tokTypes.braceR, blockIndent, line, true)) {
+        if (this.tok.type === _.tokTypes._case || this.tok.type === _.tokTypes._default) {
+          var isCase = this.tok.type === _.tokTypes._case;
+          if (cur) this.finishNode(cur, "SwitchCase");
+          node.cases.push(cur = this.startNode());
+          cur.consequent = [];
+          this.next();
+          if (isCase) cur.test = this.parseExpression();else cur.test = null;
+          this.expect(_.tokTypes.colon);
+        } else {
+          if (!cur) {
+            node.cases.push(cur = this.startNode());
+            cur.consequent = [];
+            cur.test = null;
+          }
+          cur.consequent.push(this.parseStatement());
+        }
+      }
+      if (cur) this.finishNode(cur, "SwitchCase");
+      this.popCx();
+      this.eat(_.tokTypes.braceR);
+      return this.finishNode(node, "SwitchStatement");
+
+    case _.tokTypes._throw:
+      this.next();
+      node.argument = this.parseExpression();
+      this.semicolon();
+      return this.finishNode(node, "ThrowStatement");
+
+    case _.tokTypes._try:
+      this.next();
+      node.block = this.parseBlock();
+      node.handler = null;
+      if (this.tok.type === _.tokTypes._catch) {
+        var clause = this.startNode();
+        this.next();
+        this.expect(_.tokTypes.parenL);
+        clause.param = this.toAssignable(this.parseExprAtom(), true);
+        this.expect(_.tokTypes.parenR);
+        clause.guard = null;
+        clause.body = this.parseBlock();
+        node.handler = this.finishNode(clause, "CatchClause");
+      }
+      node.finalizer = this.eat(_.tokTypes._finally) ? this.parseBlock() : null;
+      if (!node.handler && !node.finalizer) return node.block;
+      return this.finishNode(node, "TryStatement");
+
+    case _.tokTypes._var:
+    case _.tokTypes._let:
+    case _.tokTypes._const:
+      return this.parseVar();
+
+    case _.tokTypes._while:
+      this.next();
+      node.test = this.parseParenExpression();
+      node.body = this.parseStatement();
+      return this.finishNode(node, "WhileStatement");
+
+    case _.tokTypes._with:
+      this.next();
+      node.object = this.parseParenExpression();
+      node.body = this.parseStatement();
+      return this.finishNode(node, "WithStatement");
+
+    case _.tokTypes.braceL:
+      return this.parseBlock();
+
+    case _.tokTypes.semi:
+      this.next();
+      return this.finishNode(node, "EmptyStatement");
+
+    case _.tokTypes._class:
+      return this.parseClass(true);
+
+    case _.tokTypes._import:
+      return this.parseImport();
+
+    case _.tokTypes._export:
+      return this.parseExport();
+
+    default:
+      var expr = this.parseExpression();
+      if (_parseutil.isDummy(expr)) {
+        this.next();
+        if (this.tok.type === _.tokTypes.eof) return this.finishNode(node, "EmptyStatement");
+        return this.parseStatement();
+      } else if (starttype === _.tokTypes.name && expr.type === "Identifier" && this.eat(_.tokTypes.colon)) {
+        node.body = this.parseStatement();
+        node.label = expr;
+        return this.finishNode(node, "LabeledStatement");
+      } else {
+        node.expression = expr;
+        this.semicolon();
+        return this.finishNode(node, "ExpressionStatement");
+      }
+  }
+};
+
+lp.parseBlock = function () {
+  var node = this.startNode();
+  this.pushCx();
+  this.expect(_.tokTypes.braceL);
+  var blockIndent = this.curIndent,
+      line = this.curLineStart;
+  node.body = [];
+  while (!this.closes(_.tokTypes.braceR, blockIndent, line, true)) node.body.push(this.parseStatement());
+  this.popCx();
+  this.eat(_.tokTypes.braceR);
+  return this.finishNode(node, "BlockStatement");
+};
+
+lp.parseFor = function (node, init) {
+  node.init = init;
+  node.test = node.update = null;
+  if (this.eat(_.tokTypes.semi) && this.tok.type !== _.tokTypes.semi) node.test = this.parseExpression();
+  if (this.eat(_.tokTypes.semi) && this.tok.type !== _.tokTypes.parenR) node.update = this.parseExpression();
+  this.popCx();
+  this.expect(_.tokTypes.parenR);
+  node.body = this.parseStatement();
+  return this.finishNode(node, "ForStatement");
+};
+
+lp.parseForIn = function (node, init) {
+  var type = this.tok.type === _.tokTypes._in ? "ForInStatement" : "ForOfStatement";
+  this.next();
+  node.left = init;
+  node.right = this.parseExpression();
+  this.popCx();
+  this.expect(_.tokTypes.parenR);
+  node.body = this.parseStatement();
+  return this.finishNode(node, type);
+};
+
+lp.parseVar = function (noIn) {
+  var node = this.startNode();
+  node.kind = this.tok.type.keyword;
+  this.next();
+  node.declarations = [];
+  do {
+    var decl = this.startNode();
+    decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom(), true) : this.parseIdent();
+    decl.init = this.eat(_.tokTypes.eq) ? this.parseMaybeAssign(noIn) : null;
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+  } while (this.eat(_.tokTypes.comma));
+  if (!node.declarations.length) {
+    var decl = this.startNode();
+    decl.id = this.dummyIdent();
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+  }
+  if (!noIn) this.semicolon();
+  return this.finishNode(node, "VariableDeclaration");
+};
+
+lp.parseClass = function (isStatement) {
+  var node = this.startNode();
+  this.next();
+  if (this.tok.type === _.tokTypes.name) node.id = this.parseIdent();else if (isStatement) node.id = this.dummyIdent();else node.id = null;
+  node.superClass = this.eat(_.tokTypes._extends) ? this.parseExpression() : null;
+  node.body = this.startNode();
+  node.body.body = [];
+  this.pushCx();
+  var indent = this.curIndent + 1,
+      line = this.curLineStart;
+  this.eat(_.tokTypes.braceL);
+  if (this.curIndent + 1 < indent) {
+    indent = this.curIndent;line = this.curLineStart;
+  }
+  while (!this.closes(_.tokTypes.braceR, indent, line)) {
+    if (this.semicolon()) continue;
+    var method = this.startNode(),
+        isGenerator = undefined;
+    if (this.options.ecmaVersion >= 6) {
+      method["static"] = false;
+      isGenerator = this.eat(_.tokTypes.star);
+    }
+    this.parsePropertyName(method);
+    if (_parseutil.isDummy(method.key)) {
+      if (_parseutil.isDummy(this.parseMaybeAssign())) this.next();this.eat(_.tokTypes.comma);continue;
+    }
+    if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" && (this.tok.type != _.tokTypes.parenL && this.tok.type != _.tokTypes.braceL)) {
+      method["static"] = true;
+      isGenerator = this.eat(_.tokTypes.star);
+      this.parsePropertyName(method);
+    } else {
+      method["static"] = false;
+    }
+    if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" && !method.computed && (method.key.name === "get" || method.key.name === "set") && this.tok.type !== _.tokTypes.parenL && this.tok.type !== _.tokTypes.braceL) {
+      method.kind = method.key.name;
+      this.parsePropertyName(method);
+      method.value = this.parseMethod(false);
+    } else {
+      if (!method.computed && !method["static"] && !isGenerator && (method.key.type === "Identifier" && method.key.name === "constructor" || method.key.type === "Literal" && method.key.value === "constructor")) {
+        method.kind = "constructor";
+      } else {
+        method.kind = "method";
+      }
+      method.value = this.parseMethod(isGenerator);
+    }
+    node.body.body.push(this.finishNode(method, "MethodDefinition"));
+  }
+  this.popCx();
+  if (!this.eat(_.tokTypes.braceR)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start;
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
+  }
+  this.semicolon();
+  this.finishNode(node.body, "ClassBody");
+  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
+};
+
+lp.parseFunction = function (node, isStatement) {
+  this.initFunction(node);
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = this.eat(_.tokTypes.star);
+  }
+  if (this.tok.type === _.tokTypes.name) node.id = this.parseIdent();else if (isStatement) node.id = this.dummyIdent();
+  node.params = this.parseFunctionParams();
+  node.body = this.parseBlock();
+  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+};
+
+lp.parseExport = function () {
+  var node = this.startNode();
+  this.next();
+  if (this.eat(_.tokTypes.star)) {
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
+    return this.finishNode(node, "ExportAllDeclaration");
+  }
+  if (this.eat(_.tokTypes._default)) {
+    var expr = this.parseMaybeAssign();
+    if (expr.id) {
+      switch (expr.type) {
+        case "FunctionExpression":
+          expr.type = "FunctionDeclaration";break;
+        case "ClassExpression":
+          expr.type = "ClassDeclaration";break;
+      }
+    }
+    node.declaration = expr;
+    this.semicolon();
+    return this.finishNode(node, "ExportDefaultDeclaration");
+  }
+  if (this.tok.type.keyword) {
+    node.declaration = this.parseStatement();
+    node.specifiers = [];
+    node.source = null;
+  } else {
+    node.declaration = null;
+    node.specifiers = this.parseExportSpecifierList();
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
+    this.semicolon();
+  }
+  return this.finishNode(node, "ExportNamedDeclaration");
+};
+
+lp.parseImport = function () {
+  var node = this.startNode();
+  this.next();
+  if (this.tok.type === _.tokTypes.string) {
+    node.specifiers = [];
+    node.source = this.parseExprAtom();
+    node.kind = "";
+  } else {
+    var elt = undefined;
+    if (this.tok.type === _.tokTypes.name && this.tok.value !== "from") {
+      elt = this.startNode();
+      elt.local = this.parseIdent();
+      this.finishNode(elt, "ImportDefaultSpecifier");
+      this.eat(_.tokTypes.comma);
+    }
+    node.specifiers = this.parseImportSpecifierList();
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : this.dummyString();
+    if (elt) node.specifiers.unshift(elt);
+  }
+  this.semicolon();
+  return this.finishNode(node, "ImportDeclaration");
+};
+
+lp.parseImportSpecifierList = function () {
+  var elts = [];
+  if (this.tok.type === _.tokTypes.star) {
+    var elt = this.startNode();
+    this.next();
+    if (this.eatContextual("as")) elt.local = this.parseIdent();
+    elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"));
+  } else {
+    var indent = this.curIndent,
+        line = this.curLineStart,
+        continuedLine = this.nextLineStart;
+    this.pushCx();
+    this.eat(_.tokTypes.braceL);
+    if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
+    while (!this.closes(_.tokTypes.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
+      var elt = this.startNode();
+      if (this.eat(_.tokTypes.star)) {
+        if (this.eatContextual("as")) elt.local = this.parseIdent();
+        this.finishNode(elt, "ImportNamespaceSpecifier");
+      } else {
+        if (this.isContextual("from")) break;
+        elt.imported = this.parseIdent();
+        if (_parseutil.isDummy(elt.imported)) break;
+        elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported;
+        this.finishNode(elt, "ImportSpecifier");
+      }
+      elts.push(elt);
+      this.eat(_.tokTypes.comma);
+    }
+    this.eat(_.tokTypes.braceR);
+    this.popCx();
+  }
+  return elts;
+};
+
+lp.parseExportSpecifierList = function () {
+  var elts = [];
+  var indent = this.curIndent,
+      line = this.curLineStart,
+      continuedLine = this.nextLineStart;
+  this.pushCx();
+  this.eat(_.tokTypes.braceL);
+  if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
+  while (!this.closes(_.tokTypes.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
+    if (this.isContextual("from")) break;
+    var elt = this.startNode();
+    elt.local = this.parseIdent();
+    if (_parseutil.isDummy(elt.local)) break;
+    elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local;
+    this.finishNode(elt, "ExportSpecifier");
+    elts.push(elt);
+    this.eat(_.tokTypes.comma);
+  }
+  this.eat(_.tokTypes.braceR);
+  this.popCx();
+  return elts;
+};
+
+},{"..":1,"./parseutil":4,"./state":5}],7:[function(_dereq_,module,exports){
+"use strict";
+
+var _ = _dereq_("..");
+
+var _state = _dereq_("./state");
+
+var lp = _state.LooseParser.prototype;
+
+function isSpace(ch) {
+  return ch < 14 && ch > 8 || ch === 32 || ch === 160 || _.isNewLine(ch);
+}
+
+lp.next = function () {
+  this.last = this.tok;
+  if (this.ahead.length) this.tok = this.ahead.shift();else this.tok = this.readToken();
+
+  if (this.tok.start >= this.nextLineStart) {
+    while (this.tok.start >= this.nextLineStart) {
+      this.curLineStart = this.nextLineStart;
+      this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
+    }
+    this.curIndent = this.indentationAfter(this.curLineStart);
+  }
+};
+
+lp.readToken = function () {
+  for (;;) {
+    try {
+      this.toks.next();
+      if (this.toks.type === _.tokTypes.dot && this.input.substr(this.toks.end, 1) === "." && this.options.ecmaVersion >= 6) {
+        this.toks.end++;
+        this.toks.type = _.tokTypes.ellipsis;
+      }
+      return new _.Token(this.toks);
+    } catch (e) {
+      if (!(e instanceof SyntaxError)) throw e;
+
+      // Try to skip some text, based on the error message, and then continue
+      var msg = e.message,
+          pos = e.raisedAt,
+          replace = true;
+      if (/unterminated/i.test(msg)) {
+        pos = this.lineEnd(e.pos + 1);
+        if (/string/.test(msg)) {
+          replace = { start: e.pos, end: pos, type: _.tokTypes.string, value: this.input.slice(e.pos + 1, pos) };
+        } else if (/regular expr/i.test(msg)) {
+          var re = this.input.slice(e.pos, pos);
+          try {
+            re = new RegExp(re);
+          } catch (e) {}
+          replace = { start: e.pos, end: pos, type: _.tokTypes.regexp, value: re };
+        } else if (/template/.test(msg)) {
+          replace = { start: e.pos, end: pos,
+            type: _.tokTypes.template,
+            value: this.input.slice(e.pos, pos) };
+        } else {
+          replace = false;
+        }
+      } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number|expected number in radix/i.test(msg)) {
+        while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos;
+      } else if (/character escape|expected hexadecimal/i.test(msg)) {
+        while (pos < this.input.length) {
+          var ch = this.input.charCodeAt(pos++);
+          if (ch === 34 || ch === 39 || _.isNewLine(ch)) break;
+        }
+      } else if (/unexpected character/i.test(msg)) {
+        pos++;
+        replace = false;
+      } else if (/regular expression/i.test(msg)) {
+        replace = true;
+      } else {
+        throw e;
+      }
+      this.resetTo(pos);
+      if (replace === true) replace = { start: pos, end: pos, type: _.tokTypes.name, value: "✖" };
+      if (replace) {
+        if (this.options.locations) replace.loc = new _.SourceLocation(this.toks, _.getLineInfo(this.input, replace.start), _.getLineInfo(this.input, replace.end));
+        return replace;
+      }
+    }
+  }
+};
+
+lp.resetTo = function (pos) {
+  this.toks.pos = pos;
+  var ch = this.input.charAt(pos - 1);
+  this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) || /[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos));
+
+  if (this.options.locations) {
+    this.toks.curLine = 1;
+    this.toks.lineStart = _.lineBreakG.lastIndex = 0;
+    var match = undefined;
+    while ((match = _.lineBreakG.exec(this.input)) && match.index < pos) {
+      ++this.toks.curLine;
+      this.toks.lineStart = match.index + match[0].length;
+    }
+  }
+};
+
+lp.lookAhead = function (n) {
+  while (n > this.ahead.length) this.ahead.push(this.readToken());
+  return this.ahead[n - 1];
+};
+
+},{"..":1,"./state":5}]},{},[3])(3)
+});
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/walk.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/walk.js
new file mode 100644
index 0000000..d3c1784
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/dist/walk.js
@@ -0,0 +1,376 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.acorn || (g.acorn = {})).walk = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+// AST walker module for Mozilla Parser API compatible trees
+
+// A simple walk is one where you simply specify callbacks to be
+// called on specific nodes. The last two arguments are optional. A
+// simple use would be
+//
+//     walk.simple(myTree, {
+//         Expression: function(node) { ... }
+//     });
+//
+// to do something with all expressions. All Parser API node types
+// can be used to identify node types, as well as Expression,
+// Statement, and ScopeBody, which denote categories of nodes.
+//
+// The base argument can be used to pass a custom (recursive)
+// walker, and state can be used to give this walked an initial
+// state.
+
+"use strict";
+
+exports.__esModule = true;
+exports.simple = simple;
+exports.ancestor = ancestor;
+exports.recursive = recursive;
+exports.findNodeAt = findNodeAt;
+exports.findNodeAround = findNodeAround;
+exports.findNodeAfter = findNodeAfter;
+exports.findNodeBefore = findNodeBefore;
+exports.make = make;
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function simple(node, visitors, base, state, override) {
+  if (!base) base = exports.base;(function c(node, st, override) {
+    var type = override || node.type,
+        found = visitors[type];
+    base[type](node, st, c);
+    if (found) found(node, st);
+  })(node, state, override);
+}
+
+// An ancestor walk builds up an array of ancestor nodes (including
+// the current node) and passes them to the callback as the state parameter.
+
+function ancestor(node, visitors, base, state) {
+  if (!base) base = exports.base;
+  if (!state) state = [];(function c(node, st, override) {
+    var type = override || node.type,
+        found = visitors[type];
+    if (node != st[st.length - 1]) {
+      st = st.slice();
+      st.push(node);
+    }
+    base[type](node, st, c);
+    if (found) found(node, st);
+  })(node, state);
+}
+
+// A recursive walk is one where your functions override the default
+// walkers. They can modify and replace the state parameter that's
+// threaded through the walk, and can opt how and whether to walk
+// their child nodes (by calling their third argument on these
+// nodes).
+
+function recursive(node, state, funcs, base, override) {
+  var visitor = funcs ? exports.make(funcs, base) : base;(function c(node, st, override) {
+    visitor[override || node.type](node, st, c);
+  })(node, state, override);
+}
+
+function makeTest(test) {
+  if (typeof test == "string") return function (type) {
+    return type == test;
+  };else if (!test) return function () {
+    return true;
+  };else return test;
+}
+
+var Found = function Found(node, state) {
+  _classCallCheck(this, Found);
+
+  this.node = node;this.state = state;
+};
+
+// Find a node with a given start, end, and type (all are optional,
+// null can be used as wildcard). Returns a {node, state} object, or
+// undefined when it doesn't find a matching node.
+
+function findNodeAt(node, start, end, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  try {
+    ;(function c(node, st, override) {
+      var type = override || node.type;
+      if ((start == null || node.start <= start) && (end == null || node.end >= end)) base[type](node, st, c);
+      if ((start == null || node.start == start) && (end == null || node.end == end) && test(type, node)) throw new Found(node, st);
+    })(node, state);
+  } catch (e) {
+    if (e instanceof Found) return e;
+    throw e;
+  }
+}
+
+// Find the innermost node of a given type that contains the given
+// position. Interface similar to findNodeAt.
+
+function findNodeAround(node, pos, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  try {
+    ;(function c(node, st, override) {
+      var type = override || node.type;
+      if (node.start > pos || node.end < pos) return;
+      base[type](node, st, c);
+      if (test(type, node)) throw new Found(node, st);
+    })(node, state);
+  } catch (e) {
+    if (e instanceof Found) return e;
+    throw e;
+  }
+}
+
+// Find the outermost matching node after a given position.
+
+function findNodeAfter(node, pos, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  try {
+    ;(function c(node, st, override) {
+      if (node.end < pos) return;
+      var type = override || node.type;
+      if (node.start >= pos && test(type, node)) throw new Found(node, st);
+      base[type](node, st, c);
+    })(node, state);
+  } catch (e) {
+    if (e instanceof Found) return e;
+    throw e;
+  }
+}
+
+// Find the outermost matching node before a given position.
+
+function findNodeBefore(node, pos, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  var max = undefined;(function c(node, st, override) {
+    if (node.start > pos) return;
+    var type = override || node.type;
+    if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node)) max = new Found(node, st);
+    base[type](node, st, c);
+  })(node, state);
+  return max;
+}
+
+// Used to create a custom walker. Will fill in all missing node
+// type properties with the defaults.
+
+function make(funcs, base) {
+  if (!base) base = exports.base;
+  var visitor = {};
+  for (var type in base) visitor[type] = base[type];
+  for (var type in funcs) visitor[type] = funcs[type];
+  return visitor;
+}
+
+function skipThrough(node, st, c) {
+  c(node, st);
+}
+function ignore(_node, _st, _c) {}
+
+// Node walkers.
+
+var base = {};
+
+exports.base = base;
+base.Program = base.BlockStatement = function (node, st, c) {
+  for (var i = 0; i < node.body.length; ++i) {
+    c(node.body[i], st, "Statement");
+  }
+};
+base.Statement = skipThrough;
+base.EmptyStatement = ignore;
+base.ExpressionStatement = base.ParenthesizedExpression = function (node, st, c) {
+  return c(node.expression, st, "Expression");
+};
+base.IfStatement = function (node, st, c) {
+  c(node.test, st, "Expression");
+  c(node.consequent, st, "Statement");
+  if (node.alternate) c(node.alternate, st, "Statement");
+};
+base.LabeledStatement = function (node, st, c) {
+  return c(node.body, st, "Statement");
+};
+base.BreakStatement = base.ContinueStatement = ignore;
+base.WithStatement = function (node, st, c) {
+  c(node.object, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.SwitchStatement = function (node, st, c) {
+  c(node.discriminant, st, "Expression");
+  for (var i = 0; i < node.cases.length; ++i) {
+    var cs = node.cases[i];
+    if (cs.test) c(cs.test, st, "Expression");
+    for (var j = 0; j < cs.consequent.length; ++j) {
+      c(cs.consequent[j], st, "Statement");
+    }
+  }
+};
+base.ReturnStatement = base.YieldExpression = function (node, st, c) {
+  if (node.argument) c(node.argument, st, "Expression");
+};
+base.ThrowStatement = base.SpreadElement = function (node, st, c) {
+  return c(node.argument, st, "Expression");
+};
+base.TryStatement = function (node, st, c) {
+  c(node.block, st, "Statement");
+  if (node.handler) {
+    c(node.handler.param, st, "Pattern");
+    c(node.handler.body, st, "ScopeBody");
+  }
+  if (node.finalizer) c(node.finalizer, st, "Statement");
+};
+base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
+  c(node.test, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.ForStatement = function (node, st, c) {
+  if (node.init) c(node.init, st, "ForInit");
+  if (node.test) c(node.test, st, "Expression");
+  if (node.update) c(node.update, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.ForInStatement = base.ForOfStatement = function (node, st, c) {
+  c(node.left, st, "ForInit");
+  c(node.right, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.ForInit = function (node, st, c) {
+  if (node.type == "VariableDeclaration") c(node, st);else c(node, st, "Expression");
+};
+base.DebuggerStatement = ignore;
+
+base.FunctionDeclaration = function (node, st, c) {
+  return c(node, st, "Function");
+};
+base.VariableDeclaration = function (node, st, c) {
+  for (var i = 0; i < node.declarations.length; ++i) {
+    c(node.declarations[i], st);
+  }
+};
+base.VariableDeclarator = function (node, st, c) {
+  c(node.id, st, "Pattern");
+  if (node.init) c(node.init, st, "Expression");
+};
+
+base.Function = function (node, st, c) {
+  if (node.id) c(node.id, st, "Pattern");
+  for (var i = 0; i < node.params.length; i++) {
+    c(node.params[i], st, "Pattern");
+  }c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody");
+};
+// FIXME drop these node types in next major version
+// (They are awkward, and in ES6 every block can be a scope.)
+base.ScopeBody = function (node, st, c) {
+  return c(node, st, "Statement");
+};
+base.ScopeExpression = function (node, st, c) {
+  return c(node, st, "Expression");
+};
+
+base.Pattern = function (node, st, c) {
+  if (node.type == "Identifier") c(node, st, "VariablePattern");else if (node.type == "MemberExpression") c(node, st, "MemberPattern");else c(node, st);
+};
+base.VariablePattern = ignore;
+base.MemberPattern = skipThrough;
+base.RestElement = function (node, st, c) {
+  return c(node.argument, st, "Pattern");
+};
+base.ArrayPattern = function (node, st, c) {
+  for (var i = 0; i < node.elements.length; ++i) {
+    var elt = node.elements[i];
+    if (elt) c(elt, st, "Pattern");
+  }
+};
+base.ObjectPattern = function (node, st, c) {
+  for (var i = 0; i < node.properties.length; ++i) {
+    c(node.properties[i].value, st, "Pattern");
+  }
+};
+
+base.Expression = skipThrough;
+base.ThisExpression = base.Super = base.MetaProperty = ignore;
+base.ArrayExpression = function (node, st, c) {
+  for (var i = 0; i < node.elements.length; ++i) {
+    var elt = node.elements[i];
+    if (elt) c(elt, st, "Expression");
+  }
+};
+base.ObjectExpression = function (node, st, c) {
+  for (var i = 0; i < node.properties.length; ++i) {
+    c(node.properties[i], st);
+  }
+};
+base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
+base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
+  for (var i = 0; i < node.expressions.length; ++i) {
+    c(node.expressions[i], st, "Expression");
+  }
+};
+base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
+  c(node.argument, st, "Expression");
+};
+base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
+  c(node.left, st, "Expression");
+  c(node.right, st, "Expression");
+};
+base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
+  c(node.left, st, "Pattern");
+  c(node.right, st, "Expression");
+};
+base.ConditionalExpression = function (node, st, c) {
+  c(node.test, st, "Expression");
+  c(node.consequent, st, "Expression");
+  c(node.alternate, st, "Expression");
+};
+base.NewExpression = base.CallExpression = function (node, st, c) {
+  c(node.callee, st, "Expression");
+  if (node.arguments) for (var i = 0; i < node.arguments.length; ++i) {
+    c(node.arguments[i], st, "Expression");
+  }
+};
+base.MemberExpression = function (node, st, c) {
+  c(node.object, st, "Expression");
+  if (node.computed) c(node.property, st, "Expression");
+};
+base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
+  if (node.declaration) c(node.declaration, st);
+  if (node.source) c(node.source, st, "Expression");
+};
+base.ExportAllDeclaration = function (node, st, c) {
+  c(node.source, st, "Expression");
+};
+base.ImportDeclaration = function (node, st, c) {
+  for (var i = 0; i < node.specifiers.length; i++) {
+    c(node.specifiers[i], st);
+  }c(node.source, st, "Expression");
+};
+base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore;
+
+base.TaggedTemplateExpression = function (node, st, c) {
+  c(node.tag, st, "Expression");
+  c(node.quasi, st);
+};
+base.ClassDeclaration = base.ClassExpression = function (node, st, c) {
+  return c(node, st, "Class");
+};
+base.Class = function (node, st, c) {
+  if (node.id) c(node.id, st, "Pattern");
+  if (node.superClass) c(node.superClass, st, "Expression");
+  for (var i = 0; i < node.body.body.length; i++) {
+    c(node.body.body[i], st);
+  }
+};
+base.MethodDefinition = base.Property = function (node, st, c) {
+  if (node.computed) c(node.key, st, "Expression");
+  c(node.value, st, "Expression");
+};
+base.ComprehensionExpression = function (node, st, c) {
+  for (var i = 0; i < node.blocks.length; i++) {
+    c(node.blocks[i].right, st, "Expression");
+  }c(node.body, st, "Expression");
+};
+
+},{}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/package.json
new file mode 100644
index 0000000..5617ca2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/package.json
@@ -0,0 +1,185 @@
+{
+  "name": "acorn",
+  "description": "ECMAScript parser",
+  "homepage": "https://github.com/marijnh/acorn",
+  "main": "dist/acorn.js",
+  "version": "2.4.0",
+  "engines": {
+    "node": ">=0.4.0"
+  },
+  "maintainers": [
+    {
+      "name": "marijn",
+      "email": "marijnh@gmail.com"
+    },
+    {
+      "name": "rreverser",
+      "email": "me@rreverser.com"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/marijnh/acorn.git"
+  },
+  "license": "MIT",
+  "scripts": {
+    "prepublish": "node bin/build-acorn.js && node bin/without_eval > dist/acorn_csp.js",
+    "test": "node test/run.js"
+  },
+  "bin": {
+    "acorn": "./bin/acorn"
+  },
+  "devDependencies": {
+    "babel-core": "^5.6.15",
+    "babelify": "^6.1.2",
+    "browserify": "^10.2.4",
+    "browserify-derequire": "^0.9.4",
+    "unicode-7.0.0": "~0.1.5"
+  },
+  "contributors": [
+    {
+      "name": "List of Acorn contributors. Updated before every release."
+    },
+    {
+      "name": "Adrian Rakovsky"
+    },
+    {
+      "name": "Alistair Braidwood"
+    },
+    {
+      "name": "Andres Suarez"
+    },
+    {
+      "name": "Aparajita Fishman"
+    },
+    {
+      "name": "Arian Stolwijk"
+    },
+    {
+      "name": "Artem Govorov"
+    },
+    {
+      "name": "Brandon Mills"
+    },
+    {
+      "name": "Charles Hughes"
+    },
+    {
+      "name": "Conrad Irwin"
+    },
+    {
+      "name": "David Bonnet"
+    },
+    {
+      "name": "ForbesLindesay"
+    },
+    {
+      "name": "Forbes Lindesay"
+    },
+    {
+      "name": "Gilad Peleg"
+    },
+    {
+      "name": "impinball"
+    },
+    {
+      "name": "Ingvar Stepanyan"
+    },
+    {
+      "name": "Jesse McCarthy"
+    },
+    {
+      "name": "Jiaxing Wang"
+    },
+    {
+      "name": "Joel Kemp"
+    },
+    {
+      "name": "Johannes Herr"
+    },
+    {
+      "name": "Jürg Lehni"
+    },
+    {
+      "name": "keeyipchan"
+    },
+    {
+      "name": "Kevin Kwok"
+    },
+    {
+      "name": "krator"
+    },
+    {
+      "name": "Marijn Haverbeke"
+    },
+    {
+      "name": "Martin Carlberg"
+    },
+    {
+      "name": "Mathias Bynens"
+    },
+    {
+      "name": "Mathieu 'p01' Henri"
+    },
+    {
+      "name": "Max Schaefer"
+    },
+    {
+      "name": "Max Zerzouri"
+    },
+    {
+      "name": "Mihai Bazon"
+    },
+    {
+      "name": "Mike Rennie"
+    },
+    {
+      "name": "Nick Fitzgerald"
+    },
+    {
+      "name": "Oskar Schöldström"
+    },
+    {
+      "name": "Paul Harper"
+    },
+    {
+      "name": "Peter Rust"
+    },
+    {
+      "name": "PlNG"
+    },
+    {
+      "name": "r-e-d"
+    },
+    {
+      "name": "Rich Harris"
+    },
+    {
+      "name": "Sebastian McKenzie"
+    },
+    {
+      "name": "Timothy Gu"
+    },
+    {
+      "name": "zsjforcn"
+    }
+  ],
+  "gitHead": "b012acf2077198ac17786115daa446af323f17f3",
+  "bugs": {
+    "url": "https://github.com/marijnh/acorn/issues"
+  },
+  "_id": "acorn@2.4.0",
+  "_shasum": "ef0e3ac322ca516a1846452acd11a5560360aefe",
+  "_from": "acorn@>=2.1.0 <3.0.0",
+  "_npmVersion": "1.4.21",
+  "_npmUser": {
+    "name": "marijn",
+    "email": "marijnh@gmail.com"
+  },
+  "dist": {
+    "shasum": "ef0e3ac322ca516a1846452acd11a5560360aefe",
+    "tarball": "http://registry.npmjs.org/acorn/-/acorn-2.4.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/acorn/-/acorn-2.4.0.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/expression.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/expression.js
new file mode 100644
index 0000000..6b27b2a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/expression.js
@@ -0,0 +1,694 @@
+// A recursive descent parser operates by defining functions for all
+// syntactic elements, and recursively calling those, each function
+// advancing the input stream and returning an AST node. Precedence
+// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
+// instead of `(!x)[1]` is handled by the fact that the parser
+// function that parses unary prefix operators is called first, and
+// in turn calls the function that parses `[]` subscripts — that
+// way, it'll receive the node for `x[1]` already parsed, and wraps
+// *that* in the unary operator node.
+//
+// Acorn uses an [operator precedence parser][opp] to handle binary
+// operator precedence, because it is much more compact than using
+// the technique outlined above, which uses different, nesting
+// functions to specify precedence, for all of the ten binary
+// precedence levels that JavaScript defines.
+//
+// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
+
+import {types as tt} from "./tokentype"
+import {Parser} from "./state"
+import {reservedWords} from "./identifier"
+import {has} from "./util"
+
+const pp = Parser.prototype
+
+// Check if property name clashes with already added.
+// Object/class getters and setters are not allowed to clash —
+// either with each other or with an init property — and in
+// strict mode, init properties are also not allowed to be repeated.
+
+pp.checkPropClash = function(prop, propHash) {
+  if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))
+    return
+  let key = prop.key, name
+  switch (key.type) {
+  case "Identifier": name = key.name; break
+  case "Literal": name = String(key.value); break
+  default: return
+  }
+  let kind = prop.kind
+  if (this.options.ecmaVersion >= 6) {
+    if (name === "__proto__" && kind === "init") {
+      if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
+      propHash.proto = true
+    }
+    return
+  }
+  let other
+  if (has(propHash, name)) {
+    other = propHash[name]
+    let isGetSet = kind !== "init"
+    if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init))
+      this.raise(key.start, "Redefinition of property")
+  } else {
+    other = propHash[name] = {
+      init: false,
+      get: false,
+      set: false
+    }
+  }
+  other[kind] = true
+}
+
+// ### Expression parsing
+
+// These nest, from the most general expression type at the top to
+// 'atomic', nondivisible expression types at the bottom. Most of
+// the functions will simply let the function(s) below them parse,
+// and, *if* the syntactic construct they handle is present, wrap
+// the AST node that the inner parser gave them in another node.
+
+// Parse a full expression. The optional arguments are used to
+// forbid the `in` operator (in for loops initalization expressions)
+// and provide reference for storing '=' operator inside shorthand
+// property assignment in contexts where both object expression
+// and object pattern might appear (so it's possible to raise
+// delayed syntax error at correct position).
+
+pp.parseExpression = function(noIn, refShorthandDefaultPos) {
+  let startPos = this.start, startLoc = this.startLoc
+  let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos)
+  if (this.type === tt.comma) {
+    let node = this.startNodeAt(startPos, startLoc)
+    node.expressions = [expr]
+    while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos))
+    return this.finishNode(node, "SequenceExpression")
+  }
+  return expr
+}
+
+// Parse an assignment expression. This includes applications of
+// operators like `+=`.
+
+pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos, afterLeftParse) {
+  if (this.type == tt._yield && this.inGenerator) return this.parseYield()
+
+  let failOnShorthandAssign
+  if (!refShorthandDefaultPos) {
+    refShorthandDefaultPos = {start: 0}
+    failOnShorthandAssign = true
+  } else {
+    failOnShorthandAssign = false
+  }
+  let startPos = this.start, startLoc = this.startLoc
+  if (this.type == tt.parenL || this.type == tt.name)
+    this.potentialArrowAt = this.start
+  let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos)
+  if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)
+  if (this.type.isAssign) {
+    let node = this.startNodeAt(startPos, startLoc)
+    node.operator = this.value
+    node.left = this.type === tt.eq ? this.toAssignable(left) : left
+    refShorthandDefaultPos.start = 0 // reset because shorthand default was used correctly
+    this.checkLVal(left)
+    this.next()
+    node.right = this.parseMaybeAssign(noIn)
+    return this.finishNode(node, "AssignmentExpression")
+  } else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
+    this.unexpected(refShorthandDefaultPos.start)
+  }
+  return left
+}
+
+// Parse a ternary conditional (`?:`) operator.
+
+pp.parseMaybeConditional = function(noIn, refShorthandDefaultPos) {
+  let startPos = this.start, startLoc = this.startLoc
+  let expr = this.parseExprOps(noIn, refShorthandDefaultPos)
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
+  if (this.eat(tt.question)) {
+    let node = this.startNodeAt(startPos, startLoc)
+    node.test = expr
+    node.consequent = this.parseMaybeAssign()
+    this.expect(tt.colon)
+    node.alternate = this.parseMaybeAssign(noIn)
+    return this.finishNode(node, "ConditionalExpression")
+  }
+  return expr
+}
+
+// Start the precedence parser.
+
+pp.parseExprOps = function(noIn, refShorthandDefaultPos) {
+  let startPos = this.start, startLoc = this.startLoc
+  let expr = this.parseMaybeUnary(refShorthandDefaultPos)
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
+  return this.parseExprOp(expr, startPos, startLoc, -1, noIn)
+}
+
+// Parse binary operators with the operator precedence parsing
+// algorithm. `left` is the left-hand side of the operator.
+// `minPrec` provides context that allows the function to stop and
+// defer further parser to one of its callers when it encounters an
+// operator that has a lower precedence than the set it is parsing.
+
+pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
+  let prec = this.type.binop
+  if (prec != null && (!noIn || this.type !== tt._in)) {
+    if (prec > minPrec) {
+      let node = this.startNodeAt(leftStartPos, leftStartLoc)
+      node.left = left
+      node.operator = this.value
+      let op = this.type
+      this.next()
+      let startPos = this.start, startLoc = this.startLoc
+      node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn)
+      this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression")
+      return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
+    }
+  }
+  return left
+}
+
+// Parse unary operators, both prefix and postfix.
+
+pp.parseMaybeUnary = function(refShorthandDefaultPos) {
+  if (this.type.prefix) {
+    let node = this.startNode(), update = this.type === tt.incDec
+    node.operator = this.value
+    node.prefix = true
+    this.next()
+    node.argument = this.parseMaybeUnary()
+    if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)
+    if (update) this.checkLVal(node.argument)
+    else if (this.strict && node.operator === "delete" &&
+             node.argument.type === "Identifier")
+      this.raise(node.start, "Deleting local variable in strict mode")
+    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
+  }
+  let startPos = this.start, startLoc = this.startLoc
+  let expr = this.parseExprSubscripts(refShorthandDefaultPos)
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
+  while (this.type.postfix && !this.canInsertSemicolon()) {
+    let node = this.startNodeAt(startPos, startLoc)
+    node.operator = this.value
+    node.prefix = false
+    node.argument = expr
+    this.checkLVal(expr)
+    this.next()
+    expr = this.finishNode(node, "UpdateExpression")
+  }
+  return expr
+}
+
+// Parse call, dot, and `[]`-subscript expressions.
+
+pp.parseExprSubscripts = function(refShorthandDefaultPos) {
+  let startPos = this.start, startLoc = this.startLoc
+  let expr = this.parseExprAtom(refShorthandDefaultPos)
+  if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
+  return this.parseSubscripts(expr, startPos, startLoc)
+}
+
+pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
+  for (;;) {
+    if (this.eat(tt.dot)) {
+      let node = this.startNodeAt(startPos, startLoc)
+      node.object = base
+      node.property = this.parseIdent(true)
+      node.computed = false
+      base = this.finishNode(node, "MemberExpression")
+    } else if (this.eat(tt.bracketL)) {
+      let node = this.startNodeAt(startPos, startLoc)
+      node.object = base
+      node.property = this.parseExpression()
+      node.computed = true
+      this.expect(tt.bracketR)
+      base = this.finishNode(node, "MemberExpression")
+    } else if (!noCalls && this.eat(tt.parenL)) {
+      let node = this.startNodeAt(startPos, startLoc)
+      node.callee = base
+      node.arguments = this.parseExprList(tt.parenR, false)
+      base = this.finishNode(node, "CallExpression")
+    } else if (this.type === tt.backQuote) {
+      let node = this.startNodeAt(startPos, startLoc)
+      node.tag = base
+      node.quasi = this.parseTemplate()
+      base = this.finishNode(node, "TaggedTemplateExpression")
+    } else {
+      return base
+    }
+  }
+}
+
+// Parse an atomic expression — either a single token that is an
+// expression, an expression started by a keyword like `function` or
+// `new`, or an expression wrapped in punctuation like `()`, `[]`,
+// or `{}`.
+
+pp.parseExprAtom = function(refShorthandDefaultPos) {
+  let node, canBeArrow = this.potentialArrowAt == this.start
+  switch (this.type) {
+  case tt._super:
+    if (!this.inFunction)
+      this.raise(this.start, "'super' outside of function or class")
+  case tt._this:
+    let type = this.type === tt._this ? "ThisExpression" : "Super"
+    node = this.startNode()
+    this.next()
+    return this.finishNode(node, type)
+
+  case tt._yield:
+    if (this.inGenerator) this.unexpected()
+
+  case tt.name:
+    let startPos = this.start, startLoc = this.startLoc
+    let id = this.parseIdent(this.type !== tt.name)
+    if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow))
+      return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id])
+    return id
+
+  case tt.regexp:
+    let value = this.value
+    node = this.parseLiteral(value.value)
+    node.regex = {pattern: value.pattern, flags: value.flags}
+    return node
+
+  case tt.num: case tt.string:
+    return this.parseLiteral(this.value)
+
+  case tt._null: case tt._true: case tt._false:
+    node = this.startNode()
+    node.value = this.type === tt._null ? null : this.type === tt._true
+    node.raw = this.type.keyword
+    this.next()
+    return this.finishNode(node, "Literal")
+
+  case tt.parenL:
+    return this.parseParenAndDistinguishExpression(canBeArrow)
+
+  case tt.bracketL:
+    node = this.startNode()
+    this.next()
+    // check whether this is array comprehension or regular array
+    if (this.options.ecmaVersion >= 7 && this.type === tt._for) {
+      return this.parseComprehension(node, false)
+    }
+    node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos)
+    return this.finishNode(node, "ArrayExpression")
+
+  case tt.braceL:
+    return this.parseObj(false, refShorthandDefaultPos)
+
+  case tt._function:
+    node = this.startNode()
+    this.next()
+    return this.parseFunction(node, false)
+
+  case tt._class:
+    return this.parseClass(this.startNode(), false)
+
+  case tt._new:
+    return this.parseNew()
+
+  case tt.backQuote:
+    return this.parseTemplate()
+
+  default:
+    this.unexpected()
+  }
+}
+
+pp.parseLiteral = function(value) {
+  let node = this.startNode()
+  node.value = value
+  node.raw = this.input.slice(this.start, this.end)
+  this.next()
+  return this.finishNode(node, "Literal")
+}
+
+pp.parseParenExpression = function() {
+  this.expect(tt.parenL)
+  let val = this.parseExpression()
+  this.expect(tt.parenR)
+  return val
+}
+
+pp.parseParenAndDistinguishExpression = function(canBeArrow) {
+  let startPos = this.start, startLoc = this.startLoc, val
+  if (this.options.ecmaVersion >= 6) {
+    this.next()
+
+    if (this.options.ecmaVersion >= 7 && this.type === tt._for) {
+      return this.parseComprehension(this.startNodeAt(startPos, startLoc), true)
+    }
+
+    let innerStartPos = this.start, innerStartLoc = this.startLoc
+    let exprList = [], first = true
+    let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart
+    while (this.type !== tt.parenR) {
+      first ? first = false : this.expect(tt.comma)
+      if (this.type === tt.ellipsis) {
+        spreadStart = this.start
+        exprList.push(this.parseParenItem(this.parseRest()))
+        break
+      } else {
+        if (this.type === tt.parenL && !innerParenStart) {
+          innerParenStart = this.start
+        }
+        exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem))
+      }
+    }
+    let innerEndPos = this.start, innerEndLoc = this.startLoc
+    this.expect(tt.parenR)
+
+    if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
+      if (innerParenStart) this.unexpected(innerParenStart)
+      return this.parseParenArrowList(startPos, startLoc, exprList)
+    }
+
+    if (!exprList.length) this.unexpected(this.lastTokStart)
+    if (spreadStart) this.unexpected(spreadStart)
+    if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)
+
+    if (exprList.length > 1) {
+      val = this.startNodeAt(innerStartPos, innerStartLoc)
+      val.expressions = exprList
+      this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc)
+    } else {
+      val = exprList[0]
+    }
+  } else {
+    val = this.parseParenExpression()
+  }
+
+  if (this.options.preserveParens) {
+    let par = this.startNodeAt(startPos, startLoc)
+    par.expression = val
+    return this.finishNode(par, "ParenthesizedExpression")
+  } else {
+    return val
+  }
+}
+
+pp.parseParenItem = function(item) {
+  return item
+}
+
+pp.parseParenArrowList = function(startPos, startLoc, exprList) {
+  return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
+}
+
+// New's precedence is slightly tricky. It must allow its argument
+// to be a `[]` or dot subscript expression, but not a call — at
+// least, not without wrapping it in parentheses. Thus, it uses the
+
+const empty = []
+
+pp.parseNew = function() {
+  let node = this.startNode()
+  let meta = this.parseIdent(true)
+  if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
+    node.meta = meta
+    node.property = this.parseIdent(true)
+    if (node.property.name !== "target")
+      this.raise(node.property.start, "The only valid meta property for new is new.target")
+    return this.finishNode(node, "MetaProperty")
+  }
+  let startPos = this.start, startLoc = this.startLoc
+  node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)
+  if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)
+  else node.arguments = empty
+  return this.finishNode(node, "NewExpression")
+}
+
+// Parse template expression.
+
+pp.parseTemplateElement = function() {
+  let elem = this.startNode()
+  elem.value = {
+    raw: this.input.slice(this.start, this.end).replace(/\r\n?/g, '\n'),
+    cooked: this.value
+  }
+  this.next()
+  elem.tail = this.type === tt.backQuote
+  return this.finishNode(elem, "TemplateElement")
+}
+
+pp.parseTemplate = function() {
+  let node = this.startNode()
+  this.next()
+  node.expressions = []
+  let curElt = this.parseTemplateElement()
+  node.quasis = [curElt]
+  while (!curElt.tail) {
+    this.expect(tt.dollarBraceL)
+    node.expressions.push(this.parseExpression())
+    this.expect(tt.braceR)
+    node.quasis.push(curElt = this.parseTemplateElement())
+  }
+  this.next()
+  return this.finishNode(node, "TemplateLiteral")
+}
+
+// Parse an object literal or binding pattern.
+
+pp.parseObj = function(isPattern, refShorthandDefaultPos) {
+  let node = this.startNode(), first = true, propHash = {}
+  node.properties = []
+  this.next()
+  while (!this.eat(tt.braceR)) {
+    if (!first) {
+      this.expect(tt.comma)
+      if (this.afterTrailingComma(tt.braceR)) break
+    } else first = false
+
+    let prop = this.startNode(), isGenerator, startPos, startLoc
+    if (this.options.ecmaVersion >= 6) {
+      prop.method = false
+      prop.shorthand = false
+      if (isPattern || refShorthandDefaultPos) {
+        startPos = this.start
+        startLoc = this.startLoc
+      }
+      if (!isPattern)
+        isGenerator = this.eat(tt.star)
+    }
+    this.parsePropertyName(prop)
+    this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos)
+    this.checkPropClash(prop, propHash)
+    node.properties.push(this.finishNode(prop, "Property"))
+  }
+  return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
+}
+
+pp.parsePropertyValue = function(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {
+  if (this.eat(tt.colon)) {
+      prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos)
+      prop.kind = "init"
+    } else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) {
+      if (isPattern) this.unexpected()
+      prop.kind = "init"
+      prop.method = true
+      prop.value = this.parseMethod(isGenerator)
+    } else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
+               (prop.key.name === "get" || prop.key.name === "set") &&
+               (this.type != tt.comma && this.type != tt.braceR)) {
+      if (isGenerator || isPattern) this.unexpected()
+      prop.kind = prop.key.name
+      this.parsePropertyName(prop)
+      prop.value = this.parseMethod(false)
+      let paramCount = prop.kind === "get" ? 0 : 1
+      if (prop.value.params.length !== paramCount) {
+        let start = prop.value.start
+        if (prop.kind === "get")
+          this.raise(start, "getter should have no params");
+        else
+          this.raise(start, "setter should have exactly one param")
+      }
+    } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
+      prop.kind = "init"
+      if (isPattern) {
+        if (this.isKeyword(prop.key.name) ||
+            (this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||
+            (!this.options.allowReserved && this.isReservedWord(prop.key.name)))
+          this.raise(prop.key.start, "Binding " + prop.key.name)
+        prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
+      } else if (this.type === tt.eq && refShorthandDefaultPos) {
+        if (!refShorthandDefaultPos.start)
+          refShorthandDefaultPos.start = this.start
+        prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
+      } else {
+        prop.value = prop.key
+      }
+      prop.shorthand = true
+    } else this.unexpected()
+}
+
+pp.parsePropertyName = function(prop) {
+  if (this.options.ecmaVersion >= 6) {
+    if (this.eat(tt.bracketL)) {
+      prop.computed = true
+      prop.key = this.parseMaybeAssign()
+      this.expect(tt.bracketR)
+      return prop.key
+    } else {
+      prop.computed = false
+    }
+  }
+  return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)
+}
+
+// Initialize empty function node.
+
+pp.initFunction = function(node) {
+  node.id = null
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = false
+    node.expression = false
+  }
+}
+
+// Parse object or class method.
+
+pp.parseMethod = function(isGenerator) {
+  let node = this.startNode()
+  this.initFunction(node)
+  this.expect(tt.parenL)
+  node.params = this.parseBindingList(tt.parenR, false, false)
+  let allowExpressionBody
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = isGenerator
+  }
+  this.parseFunctionBody(node, false)
+  return this.finishNode(node, "FunctionExpression")
+}
+
+// Parse arrow function expression with given parameters.
+
+pp.parseArrowExpression = function(node, params) {
+  this.initFunction(node)
+  node.params = this.toAssignableList(params, true)
+  this.parseFunctionBody(node, true)
+  return this.finishNode(node, "ArrowFunctionExpression")
+}
+
+// Parse function body and check parameters.
+
+pp.parseFunctionBody = function(node, allowExpression) {
+  let isExpression = allowExpression && this.type !== tt.braceL
+
+  if (isExpression) {
+    node.body = this.parseMaybeAssign()
+    node.expression = true
+  } else {
+    // Start a new scope with regard to labels and the `inFunction`
+    // flag (restore them to their old value afterwards).
+    let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels
+    this.inFunction = true; this.inGenerator = node.generator; this.labels = []
+    node.body = this.parseBlock(true)
+    node.expression = false
+    this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels
+  }
+
+  // If this is a strict mode function, verify that argument names
+  // are not repeated, and it does not try to bind the words `eval`
+  // or `arguments`.
+  if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {
+    let nameHash = {}, oldStrict = this.strict
+    this.strict = true
+    if (node.id)
+      this.checkLVal(node.id, true)
+    for (let i = 0; i < node.params.length; i++)
+      this.checkLVal(node.params[i], true, nameHash)
+    this.strict = oldStrict
+  }
+}
+
+// Parses a comma-separated list of expressions, and returns them as
+// an array. `close` is the token type that ends the list, and
+// `allowEmpty` can be turned on to allow subsequent commas with
+// nothing in between them to be parsed as `null` (which is needed
+// for array literals).
+
+pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
+  let elts = [], first = true
+  while (!this.eat(close)) {
+    if (!first) {
+      this.expect(tt.comma)
+      if (allowTrailingComma && this.afterTrailingComma(close)) break
+    } else first = false
+
+    let elt
+    if (allowEmpty && this.type === tt.comma)
+      elt = null
+    else if (this.type === tt.ellipsis)
+      elt = this.parseSpread(refShorthandDefaultPos)
+    else
+      elt = this.parseMaybeAssign(false, refShorthandDefaultPos)
+    elts.push(elt)
+  }
+  return elts
+}
+
+// Parse the next token as an identifier. If `liberal` is true (used
+// when parsing properties), it will also convert keywords into
+// identifiers.
+
+pp.parseIdent = function(liberal) {
+  let node = this.startNode()
+  if (liberal && this.options.allowReserved == "never") liberal = false
+  if (this.type === tt.name) {
+    if (!liberal &&
+        ((!this.options.allowReserved && this.isReservedWord(this.value)) ||
+         (this.strict && reservedWords.strict(this.value)) &&
+         (this.options.ecmaVersion >= 6 ||
+          this.input.slice(this.start, this.end).indexOf("\\") == -1)))
+      this.raise(this.start, "The keyword '" + this.value + "' is reserved")
+    node.name = this.value
+  } else if (liberal && this.type.keyword) {
+    node.name = this.type.keyword
+  } else {
+    this.unexpected()
+  }
+  this.next()
+  return this.finishNode(node, "Identifier")
+}
+
+// Parses yield expression inside generator.
+
+pp.parseYield = function() {
+  let node = this.startNode()
+  this.next()
+  if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) {
+    node.delegate = false
+    node.argument = null
+  } else {
+    node.delegate = this.eat(tt.star)
+    node.argument = this.parseMaybeAssign()
+  }
+  return this.finishNode(node, "YieldExpression")
+}
+
+// Parses array and generator comprehensions.
+
+pp.parseComprehension = function(node, isGenerator) {
+  node.blocks = []
+  while (this.type === tt._for) {
+    let block = this.startNode()
+    this.next()
+    this.expect(tt.parenL)
+    block.left = this.parseBindingAtom()
+    this.checkLVal(block.left, true)
+    this.expectContextual("of")
+    block.right = this.parseExpression()
+    this.expect(tt.parenR)
+    node.blocks.push(this.finishNode(block, "ComprehensionBlock"))
+  }
+  node.filter = this.eat(tt._if) ? this.parseParenExpression() : null
+  node.body = this.parseExpression()
+  this.expect(isGenerator ? tt.parenR : tt.bracketR)
+  node.generator = isGenerator
+  return this.finishNode(node, "ComprehensionExpression")
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/identifier.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/identifier.js
new file mode 100644
index 0000000..ad2fe03
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/identifier.js
@@ -0,0 +1,129 @@
+// This is a trick taken from Esprima. It turns out that, on
+// non-Chrome browsers, to check whether a string is in a set, a
+// predicate containing a big ugly `switch` statement is faster than
+// a regular expression, and on Chrome the two are about on par.
+// This function uses `eval` (non-lexical) to produce such a
+// predicate from a space-separated string of words.
+//
+// It starts by sorting the words by length.
+
+function makePredicate(words) {
+  words = words.split(" ")
+  let f = "", cats = []
+  out: for (let i = 0; i < words.length; ++i) {
+    for (let j = 0; j < cats.length; ++j)
+      if (cats[j][0].length == words[i].length) {
+        cats[j].push(words[i])
+        continue out
+      }
+    cats.push([words[i]])
+  }
+  function compareTo(arr) {
+    if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"
+    f += "switch(str){"
+    for (let i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"
+    f += "return true}return false;"
+  }
+
+  // When there are more than three length categories, an outer
+  // switch first dispatches on the lengths, to save on comparisons.
+
+  if (cats.length > 3) {
+    cats.sort((a, b) => b.length - a.length)
+    f += "switch(str.length){"
+    for (let i = 0; i < cats.length; ++i) {
+      let cat = cats[i]
+      f += "case " + cat[0].length + ":"
+      compareTo(cat)
+    }
+    f += "}"
+
+    // Otherwise, simply generate a flat `switch` statement.
+
+  } else {
+    compareTo(words)
+  }
+  return new Function("str", f)
+}
+
+// Reserved word lists for various dialects of the language
+
+export const reservedWords = {
+  3: makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),
+  5: makePredicate("class enum extends super const export import"),
+  6: makePredicate("enum await"),
+  strict: makePredicate("implements interface let package private protected public static yield"),
+  strictBind: makePredicate("eval arguments")
+}
+
+// And the keywords
+
+var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"
+
+export const keywords = {
+  5: makePredicate(ecma5AndLessKeywords),
+  6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super")
+}
+
+// ## Character categories
+
+// Big ugly regular expressions that match characters in the
+// whitespace, identifier, and identifier-start categories. These
+// are only applied when a character is found to actually have a
+// code point above 128.
+// Generated by `tools/generate-identifier-regex.js`.
+
+let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"
+let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"
+
+const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]")
+const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]")
+
+nonASCIIidentifierStartChars = nonASCIIidentifierChars = null
+
+// These are a run-length and offset encoded representation of the
+// >0xffff code points that are a valid part of identifiers. The
+// offset starts at 0x10000, and each pair of numbers represents an
+// offset to the next range, and then a size of the range. They were
+// generated by tools/generate-identifier-regex.js
+var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541]
+var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239]
+
+// This has a complexity linear to the value of the code. The
+// assumption is that looking up astral identifier characters is
+// rare.
+function isInAstralSet(code, set) {
+  let pos = 0x10000
+  for (let i = 0; i < set.length; i += 2) {
+    pos += set[i]
+    if (pos > code) return false
+    pos += set[i + 1]
+    if (pos >= code) return true
+  }
+}
+
+// Test whether a given character code starts an identifier.
+
+export function isIdentifierStart(code, astral) {
+  if (code < 65) return code === 36
+  if (code < 91) return true
+  if (code < 97) return code === 95
+  if (code < 123) return true
+  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code))
+  if (astral === false) return false
+  return isInAstralSet(code, astralIdentifierStartCodes)
+}
+
+// Test whether a given character is part of an identifier.
+
+export function isIdentifierChar(code, astral) {
+  if (code < 48) return code === 36
+  if (code < 58) return true
+  if (code < 65) return false
+  if (code < 91) return true
+  if (code < 97) return code === 95
+  if (code < 123) return true
+  if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code))
+  if (astral === false) return false
+  return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/index.js
new file mode 100644
index 0000000..97fe236
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/index.js
@@ -0,0 +1,68 @@
+// Acorn is a tiny, fast JavaScript parser written in JavaScript.
+//
+// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
+// various contributors and released under an MIT license.
+//
+// Git repositories for Acorn are available at
+//
+//     http://marijnhaverbeke.nl/git/acorn
+//     https://github.com/marijnh/acorn.git
+//
+// Please use the [github bug tracker][ghbt] to report issues.
+//
+// [ghbt]: https://github.com/marijnh/acorn/issues
+//
+// This file defines the main parser interface. The library also comes
+// with a [error-tolerant parser][dammit] and an
+// [abstract syntax tree walker][walk], defined in other files.
+//
+// [dammit]: acorn_loose.js
+// [walk]: util/walk.js
+
+import {Parser} from "./state"
+import {getOptions} from "./options"
+import "./parseutil"
+import "./statement"
+import "./lval"
+import "./expression"
+import "./location"
+
+export {Parser, plugins} from "./state"
+export {defaultOptions} from "./options"
+export {Position, SourceLocation, getLineInfo} from "./locutil"
+export {Node} from "./node"
+export {TokenType, types as tokTypes} from "./tokentype"
+export {TokContext, types as tokContexts} from "./tokencontext"
+export {isIdentifierChar, isIdentifierStart} from "./identifier"
+export {Token} from "./tokenize"
+export {isNewLine, lineBreak, lineBreakG} from "./whitespace"
+
+export const version = "2.4.0"
+
+// The main exported interface (under `self.acorn` when in the
+// browser) is a `parse` function that takes a code string and
+// returns an abstract syntax tree as specified by [Mozilla parser
+// API][api].
+//
+// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+
+export function parse(input, options) {
+  return new Parser(options, input).parse()
+}
+
+// This function tries to parse a single expression at a given
+// offset in a string. Useful for parsing mixed-language formats
+// that embed JavaScript expressions.
+
+export function parseExpressionAt(input, pos, options) {
+  let p = new Parser(options, input, pos)
+  p.nextToken()
+  return p.parseExpression()
+}
+
+// Acorn is organized as a tokenizer and a recursive-descent parser.
+// The `tokenize` export provides an interface to the tokenizer.
+
+export function tokenizer(input, options) {
+  return new Parser(options, input)
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/location.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/location.js
new file mode 100644
index 0000000..4625dc3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/location.js
@@ -0,0 +1,24 @@
+import {Parser} from "./state"
+import {Position, getLineInfo} from "./locutil"
+
+const pp = Parser.prototype
+
+// This function is used to raise exceptions on parse errors. It
+// takes an offset integer (into the current `input`) to indicate
+// the location of the error, attaches the position to the end
+// of the error message, and then raises a `SyntaxError` with that
+// message.
+
+pp.raise = function(pos, message) {
+  let loc = getLineInfo(this.input, pos)
+  message += " (" + loc.line + ":" + loc.column + ")"
+  let err = new SyntaxError(message)
+  err.pos = pos; err.loc = loc; err.raisedAt = this.pos
+  throw err
+}
+
+pp.curPosition = function() {
+  if (this.options.locations) {
+    return new Position(this.curLine, this.pos - this.lineStart)
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/locutil.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/locutil.js
new file mode 100644
index 0000000..5a9465e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/locutil.js
@@ -0,0 +1,42 @@
+import {lineBreakG} from "./whitespace"
+
+// These are used when `options.locations` is on, for the
+// `startLoc` and `endLoc` properties.
+
+export class Position {
+  constructor(line, col) {
+    this.line = line
+    this.column = col
+  }
+
+  offset(n) {
+    return new Position(this.line, this.column + n)
+  }
+}
+
+export class SourceLocation {
+  constructor(p, start, end) {
+    this.start = start
+    this.end = end
+    if (p.sourceFile !== null) this.source = p.sourceFile
+  }
+}
+
+// The `getLineInfo` function is mostly useful when the
+// `locations` option is off (for performance reasons) and you
+// want to find the line/column position for a given character
+// offset. `input` should be the code string that the offset refers
+// into.
+
+export function getLineInfo(input, offset) {
+  for (let line = 1, cur = 0;;) {
+    lineBreakG.lastIndex = cur
+    let match = lineBreakG.exec(input)
+    if (match && match.index < offset) {
+      ++line
+      cur = match.index + match[0].length
+    } else {
+      return new Position(line, offset - cur)
+    }
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/acorn_loose.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/acorn_loose.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/expression.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/expression.js
new file mode 100644
index 0000000..4db42de
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/expression.js
@@ -0,0 +1,512 @@
+import {LooseParser} from "./state"
+import {isDummy} from "./parseutil"
+import {tokTypes as tt} from ".."
+
+const lp = LooseParser.prototype
+
+lp.checkLVal = function(expr, binding) {
+  if (!expr) return expr
+  switch (expr.type) {
+  case "Identifier":
+    return expr
+
+  case "MemberExpression":
+    return binding ? this.dummyIdent() : expr
+
+  case "ParenthesizedExpression":
+    expr.expression = this.checkLVal(expr.expression, binding)
+    return expr
+
+  // FIXME recursively check contents
+  case "ObjectPattern":
+  case "ArrayPattern":
+  case "RestElement":
+  case "AssignmentPattern":
+    if (this.options.ecmaVersion >= 6) return expr
+
+  default:
+    return this.dummyIdent()
+  }
+}
+
+lp.parseExpression = function(noIn) {
+  let start = this.storeCurrentPos()
+  let expr = this.parseMaybeAssign(noIn)
+  if (this.tok.type === tt.comma) {
+    let node = this.startNodeAt(start)
+    node.expressions = [expr]
+    while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn))
+    return this.finishNode(node, "SequenceExpression")
+  }
+  return expr
+}
+
+lp.parseParenExpression = function() {
+  this.pushCx()
+  this.expect(tt.parenL)
+  let val = this.parseExpression()
+  this.popCx()
+  this.expect(tt.parenR)
+  return val
+}
+
+lp.parseMaybeAssign = function(noIn) {
+  let start = this.storeCurrentPos()
+  let left = this.parseMaybeConditional(noIn)
+  if (this.tok.type.isAssign) {
+    let node = this.startNodeAt(start)
+    node.operator = this.tok.value
+    node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left)
+    this.next()
+    node.right = this.parseMaybeAssign(noIn)
+    return this.finishNode(node, "AssignmentExpression")
+  }
+  return left
+}
+
+lp.parseMaybeConditional = function(noIn) {
+  let start = this.storeCurrentPos()
+  let expr = this.parseExprOps(noIn)
+  if (this.eat(tt.question)) {
+    let node = this.startNodeAt(start)
+    node.test = expr
+    node.consequent = this.parseMaybeAssign()
+    node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent()
+    return this.finishNode(node, "ConditionalExpression")
+  }
+  return expr
+}
+
+lp.parseExprOps = function(noIn) {
+  let start = this.storeCurrentPos()
+  let indent = this.curIndent, line = this.curLineStart
+  return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line)
+}
+
+lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
+  if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left
+  let prec = this.tok.type.binop
+  if (prec != null && (!noIn || this.tok.type !== tt._in)) {
+    if (prec > minPrec) {
+      let node = this.startNodeAt(start)
+      node.left = left
+      node.operator = this.tok.value
+      this.next()
+      if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
+        node.right = this.dummyIdent()
+      } else {
+        let rightStart = this.storeCurrentPos()
+        node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line)
+      }
+      this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression")
+      return this.parseExprOp(node, start, minPrec, noIn, indent, line)
+    }
+  }
+  return left
+}
+
+lp.parseMaybeUnary = function(noIn) {
+  if (this.tok.type.prefix) {
+    let node = this.startNode(), update = this.tok.type === tt.incDec
+    node.operator = this.tok.value
+    node.prefix = true
+    this.next()
+    node.argument = this.parseMaybeUnary(noIn)
+    if (update) node.argument = this.checkLVal(node.argument)
+    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
+  } else if (this.tok.type === tt.ellipsis) {
+    let node = this.startNode()
+    this.next()
+    node.argument = this.parseMaybeUnary(noIn)
+    return this.finishNode(node, "SpreadElement")
+  }
+  let start = this.storeCurrentPos()
+  let expr = this.parseExprSubscripts()
+  while (this.tok.type.postfix && !this.canInsertSemicolon()) {
+    let node = this.startNodeAt(start)
+    node.operator = this.tok.value
+    node.prefix = false
+    node.argument = this.checkLVal(expr)
+    this.next()
+    expr = this.finishNode(node, "UpdateExpression")
+  }
+  return expr
+}
+
+lp.parseExprSubscripts = function() {
+  let start = this.storeCurrentPos()
+  return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart)
+}
+
+lp.parseSubscripts = function(base, start, noCalls, startIndent, line) {
+  for (;;) {
+    if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) {
+      if (this.tok.type == tt.dot && this.curIndent == startIndent)
+        --startIndent
+      else
+        return base
+    }
+
+    if (this.eat(tt.dot)) {
+      let node = this.startNodeAt(start)
+      node.object = base
+      if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine())
+        node.property = this.dummyIdent()
+      else
+        node.property = this.parsePropertyAccessor() || this.dummyIdent()
+      node.computed = false
+      base = this.finishNode(node, "MemberExpression")
+    } else if (this.tok.type == tt.bracketL) {
+      this.pushCx()
+      this.next()
+      let node = this.startNodeAt(start)
+      node.object = base
+      node.property = this.parseExpression()
+      node.computed = true
+      this.popCx()
+      this.expect(tt.bracketR)
+      base = this.finishNode(node, "MemberExpression")
+    } else if (!noCalls && this.tok.type == tt.parenL) {
+      let node = this.startNodeAt(start)
+      node.callee = base
+      node.arguments = this.parseExprList(tt.parenR)
+      base = this.finishNode(node, "CallExpression")
+    } else if (this.tok.type == tt.backQuote) {
+      let node = this.startNodeAt(start)
+      node.tag = base
+      node.quasi = this.parseTemplate()
+      base = this.finishNode(node, "TaggedTemplateExpression")
+    } else {
+      return base
+    }
+  }
+}
+
+lp.parseExprAtom = function() {
+  let node
+  switch (this.tok.type) {
+  case tt._this:
+  case tt._super:
+    let type = this.tok.type === tt._this ? "ThisExpression" : "Super"
+    node = this.startNode()
+    this.next()
+    return this.finishNode(node, type)
+
+  case tt.name:
+    let start = this.storeCurrentPos()
+    let id = this.parseIdent()
+    return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id
+
+  case tt.regexp:
+    node = this.startNode()
+    let val = this.tok.value
+    node.regex = {pattern: val.pattern, flags: val.flags}
+    node.value = val.value
+    node.raw = this.input.slice(this.tok.start, this.tok.end)
+    this.next()
+    return this.finishNode(node, "Literal")
+
+  case tt.num: case tt.string:
+    node = this.startNode()
+    node.value = this.tok.value
+    node.raw = this.input.slice(this.tok.start, this.tok.end)
+    this.next()
+    return this.finishNode(node, "Literal")
+
+  case tt._null: case tt._true: case tt._false:
+    node = this.startNode()
+    node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true
+    node.raw = this.tok.type.keyword
+    this.next()
+    return this.finishNode(node, "Literal")
+
+  case tt.parenL:
+    let parenStart = this.storeCurrentPos()
+    this.next()
+    let inner = this.parseExpression()
+    this.expect(tt.parenR)
+    if (this.eat(tt.arrow)) {
+      return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (isDummy(inner) ? [] : [inner]))
+    }
+    if (this.options.preserveParens) {
+      let par = this.startNodeAt(parenStart)
+      par.expression = inner
+      inner = this.finishNode(par, "ParenthesizedExpression")
+    }
+    return inner
+
+  case tt.bracketL:
+    node = this.startNode()
+    node.elements = this.parseExprList(tt.bracketR, true)
+    return this.finishNode(node, "ArrayExpression")
+
+  case tt.braceL:
+    return this.parseObj()
+
+  case tt._class:
+    return this.parseClass()
+
+  case tt._function:
+    node = this.startNode()
+    this.next()
+    return this.parseFunction(node, false)
+
+  case tt._new:
+    return this.parseNew()
+
+  case tt._yield:
+    node = this.startNode()
+    this.next()
+    if (this.semicolon() || this.canInsertSemicolon() || (this.tok.type != tt.star && !this.tok.type.startsExpr)) {
+      node.delegate = false
+      node.argument = null
+    } else {
+      node.delegate = this.eat(tt.star)
+      node.argument = this.parseMaybeAssign()
+    }
+    return this.finishNode(node, "YieldExpression")
+
+  case tt.backQuote:
+    return this.parseTemplate()
+
+  default:
+    return this.dummyIdent()
+  }
+}
+
+lp.parseNew = function() {
+  let node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart
+  let meta = this.parseIdent(true)
+  if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
+    node.meta = meta
+    node.property = this.parseIdent(true)
+    return this.finishNode(node, "MetaProperty")
+  }
+  let start = this.storeCurrentPos()
+  node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line)
+  if (this.tok.type == tt.parenL) {
+    node.arguments = this.parseExprList(tt.parenR)
+  } else {
+    node.arguments = []
+  }
+  return this.finishNode(node, "NewExpression")
+}
+
+lp.parseTemplateElement = function() {
+  let elem = this.startNode()
+  elem.value = {
+    raw: this.input.slice(this.tok.start, this.tok.end).replace(/\r\n?/g, '\n'),
+    cooked: this.tok.value
+  }
+  this.next()
+  elem.tail = this.tok.type === tt.backQuote
+  return this.finishNode(elem, "TemplateElement")
+}
+
+lp.parseTemplate = function() {
+  let node = this.startNode()
+  this.next()
+  node.expressions = []
+  let curElt = this.parseTemplateElement()
+  node.quasis = [curElt]
+  while (!curElt.tail) {
+    this.next()
+    node.expressions.push(this.parseExpression())
+    if (this.expect(tt.braceR)) {
+      curElt = this.parseTemplateElement()
+    } else {
+      curElt = this.startNode()
+      curElt.value = {cooked: '', raw: ''}
+      curElt.tail = true
+    }
+    node.quasis.push(curElt)
+  }
+  this.expect(tt.backQuote)
+  return this.finishNode(node, "TemplateLiteral")
+}
+
+lp.parseObj = function() {
+  let node = this.startNode()
+  node.properties = []
+  this.pushCx()
+  let indent = this.curIndent + 1, line = this.curLineStart
+  this.eat(tt.braceL)
+  if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
+  while (!this.closes(tt.braceR, indent, line)) {
+    let prop = this.startNode(), isGenerator, start
+    if (this.options.ecmaVersion >= 6) {
+      start = this.storeCurrentPos()
+      prop.method = false
+      prop.shorthand = false
+      isGenerator = this.eat(tt.star)
+    }
+    this.parsePropertyName(prop)
+    if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
+    if (this.eat(tt.colon)) {
+      prop.kind = "init"
+      prop.value = this.parseMaybeAssign()
+    } else if (this.options.ecmaVersion >= 6 && (this.tok.type === tt.parenL || this.tok.type === tt.braceL)) {
+      prop.kind = "init"
+      prop.method = true
+      prop.value = this.parseMethod(isGenerator)
+    } else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
+               !prop.computed && (prop.key.name === "get" || prop.key.name === "set") &&
+               (this.tok.type != tt.comma && this.tok.type != tt.braceR)) {
+      prop.kind = prop.key.name
+      this.parsePropertyName(prop)
+      prop.value = this.parseMethod(false)
+    } else {
+      prop.kind = "init"
+      if (this.options.ecmaVersion >= 6) {
+        if (this.eat(tt.eq)) {
+          let assign = this.startNodeAt(start)
+          assign.operator = "="
+          assign.left = prop.key
+          assign.right = this.parseMaybeAssign()
+          prop.value = this.finishNode(assign, "AssignmentExpression")
+        } else {
+          prop.value = prop.key
+        }
+      } else {
+        prop.value = this.dummyIdent()
+      }
+      prop.shorthand = true
+    }
+    node.properties.push(this.finishNode(prop, "Property"))
+    this.eat(tt.comma)
+  }
+  this.popCx()
+  if (!this.eat(tt.braceR)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start
+  }
+  return this.finishNode(node, "ObjectExpression")
+}
+
+lp.parsePropertyName = function(prop) {
+  if (this.options.ecmaVersion >= 6) {
+    if (this.eat(tt.bracketL)) {
+      prop.computed = true
+      prop.key = this.parseExpression()
+      this.expect(tt.bracketR)
+      return
+    } else {
+      prop.computed = false
+    }
+  }
+  let key = (this.tok.type === tt.num || this.tok.type === tt.string) ? this.parseExprAtom() : this.parseIdent()
+  prop.key = key || this.dummyIdent()
+}
+
+lp.parsePropertyAccessor = function() {
+  if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent()
+}
+
+lp.parseIdent = function() {
+  let name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword
+  if (!name) return this.dummyIdent()
+  let node = this.startNode()
+  this.next()
+  node.name = name
+  return this.finishNode(node, "Identifier")
+}
+
+lp.initFunction = function(node) {
+  node.id = null
+  node.params = []
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = false
+    node.expression = false
+  }
+}
+
+// Convert existing expression atom to assignable pattern
+// if possible.
+
+lp.toAssignable = function(node, binding) {
+  if (this.options.ecmaVersion >= 6 && node) {
+    switch (node.type) {
+    case "ObjectExpression":
+      node.type = "ObjectPattern"
+      let props = node.properties
+      for (let i = 0; i < props.length; i++)
+        this.toAssignable(props[i].value, binding)
+      break
+
+    case "ArrayExpression":
+      node.type = "ArrayPattern"
+      this.toAssignableList(node.elements, binding)
+      break
+
+    case "SpreadElement":
+      node.type = "RestElement"
+      node.argument = this.toAssignable(node.argument, binding)
+      break
+
+    case "AssignmentExpression":
+      node.type = "AssignmentPattern"
+      delete node.operator
+      break
+    }
+  }
+  return this.checkLVal(node, binding)
+}
+
+lp.toAssignableList = function(exprList, binding) {
+  for (let i = 0; i < exprList.length; i++)
+    exprList[i] = this.toAssignable(exprList[i], binding)
+  return exprList
+}
+
+lp.parseFunctionParams = function(params) {
+  params = this.parseExprList(tt.parenR)
+  return this.toAssignableList(params, true)
+}
+
+lp.parseMethod = function(isGenerator) {
+  let node = this.startNode()
+  this.initFunction(node)
+  node.params = this.parseFunctionParams()
+  node.generator = isGenerator || false
+  node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL
+  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock()
+  return this.finishNode(node, "FunctionExpression")
+}
+
+lp.parseArrowExpression = function(node, params) {
+  this.initFunction(node)
+  node.params = this.toAssignableList(params, true)
+  node.expression = this.tok.type !== tt.braceL
+  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock()
+  return this.finishNode(node, "ArrowFunctionExpression")
+}
+
+lp.parseExprList = function(close, allowEmpty) {
+  this.pushCx()
+  let indent = this.curIndent, line = this.curLineStart, elts = []
+  this.next(); // Opening bracket
+  while (!this.closes(close, indent + 1, line)) {
+    if (this.eat(tt.comma)) {
+      elts.push(allowEmpty ? null : this.dummyIdent())
+      continue
+    }
+    let elt = this.parseMaybeAssign()
+    if (isDummy(elt)) {
+      if (this.closes(close, indent, line)) break
+      this.next()
+    } else {
+      elts.push(elt)
+    }
+    this.eat(tt.comma)
+  }
+  this.popCx()
+  if (!this.eat(close)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start
+  }
+  return elts
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/index.js
new file mode 100644
index 0000000..a1a749e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/index.js
@@ -0,0 +1,49 @@
+// Acorn: Loose parser
+//
+// This module provides an alternative parser (`parse_dammit`) that
+// exposes that same interface as `parse`, but will try to parse
+// anything as JavaScript, repairing syntax error the best it can.
+// There are circumstances in which it will raise an error and give
+// up, but they are very rare. The resulting AST will be a mostly
+// valid JavaScript AST (as per the [Mozilla parser API][api], except
+// that:
+//
+// - Return outside functions is allowed
+//
+// - Label consistency (no conflicts, break only to existing labels)
+//   is not enforced.
+//
+// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
+//   the parser got too confused to return anything meaningful.
+//
+// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+//
+// The expected use for this is to *first* try `acorn.parse`, and only
+// if that fails switch to `parse_dammit`. The loose parser might
+// parse badly indented code incorrectly, so **don't** use it as
+// your default parser.
+//
+// Quite a lot of acorn.js is duplicated here. The alternative was to
+// add a *lot* of extra cruft to that file, making it less readable
+// and slower. Copying and editing the code allowed me to make
+// invasive changes and simplifications without creating a complicated
+// tangle.
+
+import * as acorn from ".."
+import {LooseParser} from "./state"
+import "./tokenize"
+import "./statement"
+import "./expression"
+
+export {LooseParser} from "./state"
+
+acorn.defaultOptions.tabSize = 4
+
+export function parse_dammit(input, options) {
+  let p = new LooseParser(input, options)
+  p.next()
+  return p.parseTopLevel()
+}
+
+acorn.parse_dammit = parse_dammit
+acorn.LooseParser = LooseParser
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/parseutil.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/parseutil.js
new file mode 100644
index 0000000..c5ee096
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/parseutil.js
@@ -0,0 +1 @@
+export function isDummy(node) { return node.name == "✖" }
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/state.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/state.js
new file mode 100644
index 0000000..85331ae
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/state.js
@@ -0,0 +1,130 @@
+import {tokenizer, SourceLocation, tokTypes as tt, Node, lineBreak, isNewLine} from ".."
+
+export class LooseParser{
+  constructor(input, options) {
+    this.toks = tokenizer(input, options)
+    this.options = this.toks.options
+    this.input = this.toks.input
+    this.tok = this.last = {type: tt.eof, start: 0, end: 0}
+    if (this.options.locations) {
+      let here = this.toks.curPosition()
+      this.tok.loc = new SourceLocation(this.toks, here, here)
+    }
+    this.ahead = []; // Tokens ahead
+    this.context = []; // Indentation contexted
+    this.curIndent = 0
+    this.curLineStart = 0
+    this.nextLineStart = this.lineEnd(this.curLineStart) + 1
+  }
+
+  startNode() {
+    return new Node(this.toks, this.tok.start, this.options.locations ? this.tok.loc.start : null)
+  }
+
+  storeCurrentPos() {
+    return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start
+  }
+
+  startNodeAt(pos) {
+    if (this.options.locations) {
+      return new Node(this.toks, pos[0], pos[1])
+    } else {
+      return new Node(this.toks, pos)
+    }
+  }
+
+  finishNode(node, type) {
+    node.type = type
+    node.end = this.last.end
+    if (this.options.locations)
+      node.loc.end = this.last.loc.end
+    if (this.options.ranges)
+      node.range[1] = this.last.end
+    return node
+  }
+
+  dummyIdent() {
+    let dummy = this.startNode()
+    dummy.name = "✖"
+    return this.finishNode(dummy, "Identifier")
+  }
+
+  dummyString() {
+    let dummy = this.startNode()
+    dummy.value = dummy.raw = "✖"
+    return this.finishNode(dummy, "Literal")
+  }
+
+  eat(type) {
+    if (this.tok.type === type) {
+      this.next()
+      return true
+    } else {
+      return false
+    }
+  }
+
+  isContextual(name) {
+    return this.tok.type === tt.name && this.tok.value === name
+  }
+
+  eatContextual(name) {
+    return this.tok.value === name && this.eat(tt.name)
+  }
+
+  canInsertSemicolon() {
+    return this.tok.type === tt.eof || this.tok.type === tt.braceR ||
+      lineBreak.test(this.input.slice(this.last.end, this.tok.start))
+  }
+
+  semicolon() {
+    return this.eat(tt.semi)
+  }
+
+  expect(type) {
+    if (this.eat(type)) return true
+    for (let i = 1; i <= 2; i++) {
+      if (this.lookAhead(i).type == type) {
+        for (let j = 0; j < i; j++) this.next()
+        return true
+      }
+    }
+  }
+
+  pushCx() {
+    this.context.push(this.curIndent)
+  }
+
+  popCx() {
+    this.curIndent = this.context.pop()
+  }
+
+  lineEnd(pos) {
+    while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos
+    return pos
+  }
+
+  indentationAfter(pos) {
+    for (let count = 0;; ++pos) {
+      let ch = this.input.charCodeAt(pos)
+      if (ch === 32) ++count
+      else if (ch === 9) count += this.options.tabSize
+      else return count
+    }
+  }
+
+  closes(closeTok, indent, line, blockHeuristic) {
+    if (this.tok.type === closeTok || this.tok.type === tt.eof) return true
+    return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() &&
+      (!blockHeuristic || this.nextLineStart >= this.input.length ||
+       this.indentationAfter(this.nextLineStart) < indent)
+  }
+
+  tokenStartsLine() {
+    for (let p = this.tok.start - 1; p >= this.curLineStart; --p) {
+      let ch = this.input.charCodeAt(p)
+      if (ch !== 9 && ch !== 32) return false
+    }
+    return true
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/statement.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/statement.js
new file mode 100644
index 0000000..41a6f61
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/statement.js
@@ -0,0 +1,421 @@
+import {LooseParser} from "./state"
+import {isDummy} from "./parseutil"
+import {getLineInfo, tokTypes as tt} from ".."
+
+const lp = LooseParser.prototype
+
+lp.parseTopLevel = function() {
+  let node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0)
+  node.body = []
+  while (this.tok.type !== tt.eof) node.body.push(this.parseStatement())
+  this.last = this.tok
+  if (this.options.ecmaVersion >= 6) {
+    node.sourceType = this.options.sourceType
+  }
+  return this.finishNode(node, "Program")
+}
+
+lp.parseStatement = function() {
+  let starttype = this.tok.type, node = this.startNode()
+
+  switch (starttype) {
+  case tt._break: case tt._continue:
+    this.next()
+    let isBreak = starttype === tt._break
+    if (this.semicolon() || this.canInsertSemicolon()) {
+      node.label = null
+    } else {
+      node.label = this.tok.type === tt.name ? this.parseIdent() : null
+      this.semicolon()
+    }
+    return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
+
+  case tt._debugger:
+    this.next()
+    this.semicolon()
+    return this.finishNode(node, "DebuggerStatement")
+
+  case tt._do:
+    this.next()
+    node.body = this.parseStatement()
+    node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent()
+    this.semicolon()
+    return this.finishNode(node, "DoWhileStatement")
+
+  case tt._for:
+    this.next()
+    this.pushCx()
+    this.expect(tt.parenL)
+    if (this.tok.type === tt.semi) return this.parseFor(node, null)
+    if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) {
+      let init = this.parseVar(true)
+      if (init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) {
+        return this.parseForIn(node, init)
+      }
+      return this.parseFor(node, init)
+    }
+    let init = this.parseExpression(true)
+    if (this.tok.type === tt._in || this.isContextual("of"))
+      return this.parseForIn(node, this.toAssignable(init))
+    return this.parseFor(node, init)
+
+  case tt._function:
+    this.next()
+    return this.parseFunction(node, true)
+
+  case tt._if:
+    this.next()
+    node.test = this.parseParenExpression()
+    node.consequent = this.parseStatement()
+    node.alternate = this.eat(tt._else) ? this.parseStatement() : null
+    return this.finishNode(node, "IfStatement")
+
+  case tt._return:
+    this.next()
+    if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null
+    else { node.argument = this.parseExpression(); this.semicolon() }
+    return this.finishNode(node, "ReturnStatement")
+
+  case tt._switch:
+    let blockIndent = this.curIndent, line = this.curLineStart
+    this.next()
+    node.discriminant = this.parseParenExpression()
+    node.cases = []
+    this.pushCx()
+    this.expect(tt.braceL)
+
+    let cur
+    while (!this.closes(tt.braceR, blockIndent, line, true)) {
+      if (this.tok.type === tt._case || this.tok.type === tt._default) {
+        let isCase = this.tok.type === tt._case
+        if (cur) this.finishNode(cur, "SwitchCase")
+        node.cases.push(cur = this.startNode())
+        cur.consequent = []
+        this.next()
+        if (isCase) cur.test = this.parseExpression()
+        else cur.test = null
+        this.expect(tt.colon)
+      } else {
+        if (!cur) {
+          node.cases.push(cur = this.startNode())
+          cur.consequent = []
+          cur.test = null
+        }
+        cur.consequent.push(this.parseStatement())
+      }
+    }
+    if (cur) this.finishNode(cur, "SwitchCase")
+    this.popCx()
+    this.eat(tt.braceR)
+    return this.finishNode(node, "SwitchStatement")
+
+  case tt._throw:
+    this.next()
+    node.argument = this.parseExpression()
+    this.semicolon()
+    return this.finishNode(node, "ThrowStatement")
+
+  case tt._try:
+    this.next()
+    node.block = this.parseBlock()
+    node.handler = null
+    if (this.tok.type === tt._catch) {
+      let clause = this.startNode()
+      this.next()
+      this.expect(tt.parenL)
+      clause.param = this.toAssignable(this.parseExprAtom(), true)
+      this.expect(tt.parenR)
+      clause.guard = null
+      clause.body = this.parseBlock()
+      node.handler = this.finishNode(clause, "CatchClause")
+    }
+    node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
+    if (!node.handler && !node.finalizer) return node.block
+    return this.finishNode(node, "TryStatement")
+
+  case tt._var:
+  case tt._let:
+  case tt._const:
+    return this.parseVar()
+
+  case tt._while:
+    this.next()
+    node.test = this.parseParenExpression()
+    node.body = this.parseStatement()
+    return this.finishNode(node, "WhileStatement")
+
+  case tt._with:
+    this.next()
+    node.object = this.parseParenExpression()
+    node.body = this.parseStatement()
+    return this.finishNode(node, "WithStatement")
+
+  case tt.braceL:
+    return this.parseBlock()
+
+  case tt.semi:
+    this.next()
+    return this.finishNode(node, "EmptyStatement")
+
+  case tt._class:
+    return this.parseClass(true)
+
+  case tt._import:
+    return this.parseImport()
+
+  case tt._export:
+    return this.parseExport()
+
+  default:
+    let expr = this.parseExpression()
+    if (isDummy(expr)) {
+      this.next()
+      if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement")
+      return this.parseStatement()
+    } else if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) {
+      node.body = this.parseStatement()
+      node.label = expr
+      return this.finishNode(node, "LabeledStatement")
+    } else {
+      node.expression = expr
+      this.semicolon()
+      return this.finishNode(node, "ExpressionStatement")
+    }
+  }
+}
+
+lp.parseBlock = function() {
+  let node = this.startNode()
+  this.pushCx()
+  this.expect(tt.braceL)
+  let blockIndent = this.curIndent, line = this.curLineStart
+  node.body = []
+  while (!this.closes(tt.braceR, blockIndent, line, true))
+    node.body.push(this.parseStatement())
+  this.popCx()
+  this.eat(tt.braceR)
+  return this.finishNode(node, "BlockStatement")
+}
+
+lp.parseFor = function(node, init) {
+  node.init = init
+  node.test = node.update = null
+  if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression()
+  if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression()
+  this.popCx()
+  this.expect(tt.parenR)
+  node.body = this.parseStatement()
+  return this.finishNode(node, "ForStatement")
+}
+
+lp.parseForIn = function(node, init) {
+  let type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement"
+  this.next()
+  node.left = init
+  node.right = this.parseExpression()
+  this.popCx()
+  this.expect(tt.parenR)
+  node.body = this.parseStatement()
+  return this.finishNode(node, type)
+}
+
+lp.parseVar = function(noIn) {
+  let node = this.startNode()
+  node.kind = this.tok.type.keyword
+  this.next()
+  node.declarations = []
+  do {
+    let decl = this.startNode()
+    decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom(), true) : this.parseIdent()
+    decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
+  } while (this.eat(tt.comma))
+  if (!node.declarations.length) {
+    let decl = this.startNode()
+    decl.id = this.dummyIdent()
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
+  }
+  if (!noIn) this.semicolon()
+  return this.finishNode(node, "VariableDeclaration")
+}
+
+lp.parseClass = function(isStatement) {
+  let node = this.startNode()
+  this.next()
+  if (this.tok.type === tt.name) node.id = this.parseIdent()
+  else if (isStatement) node.id = this.dummyIdent()
+  else node.id = null
+  node.superClass = this.eat(tt._extends) ? this.parseExpression() : null
+  node.body = this.startNode()
+  node.body.body = []
+  this.pushCx()
+  let indent = this.curIndent + 1, line = this.curLineStart
+  this.eat(tt.braceL)
+  if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
+  while (!this.closes(tt.braceR, indent, line)) {
+    if (this.semicolon()) continue
+    let method = this.startNode(), isGenerator
+    if (this.options.ecmaVersion >= 6) {
+      method.static = false
+      isGenerator = this.eat(tt.star)
+    }
+    this.parsePropertyName(method)
+    if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
+    if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" &&
+        (this.tok.type != tt.parenL && this.tok.type != tt.braceL)) {
+      method.static = true
+      isGenerator = this.eat(tt.star)
+      this.parsePropertyName(method)
+    } else {
+      method.static = false
+    }
+    if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" &&
+        !method.computed && (method.key.name === "get" || method.key.name === "set") &&
+        this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) {
+      method.kind = method.key.name
+      this.parsePropertyName(method)
+      method.value = this.parseMethod(false)
+    } else {
+      if (!method.computed && !method.static && !isGenerator && (
+        method.key.type === "Identifier" && method.key.name === "constructor" ||
+          method.key.type === "Literal" && method.key.value === "constructor")) {
+        method.kind = "constructor"
+      } else {
+        method.kind =  "method"
+      }
+      method.value = this.parseMethod(isGenerator)
+    }
+    node.body.body.push(this.finishNode(method, "MethodDefinition"))
+  }
+  this.popCx()
+  if (!this.eat(tt.braceR)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start
+  }
+  this.semicolon()
+  this.finishNode(node.body, "ClassBody")
+  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
+}
+
+lp.parseFunction = function(node, isStatement) {
+  this.initFunction(node)
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = this.eat(tt.star)
+  }
+  if (this.tok.type === tt.name) node.id = this.parseIdent()
+  else if (isStatement) node.id = this.dummyIdent()
+  node.params = this.parseFunctionParams()
+  node.body = this.parseBlock()
+  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
+}
+
+lp.parseExport = function() {
+  let node = this.startNode()
+  this.next()
+  if (this.eat(tt.star)) {
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null
+    return this.finishNode(node, "ExportAllDeclaration")
+  }
+  if (this.eat(tt._default)) {
+    let expr = this.parseMaybeAssign()
+    if (expr.id) {
+      switch (expr.type) {
+      case "FunctionExpression": expr.type = "FunctionDeclaration"; break
+      case "ClassExpression": expr.type = "ClassDeclaration"; break
+      }
+    }
+    node.declaration = expr
+    this.semicolon()
+    return this.finishNode(node, "ExportDefaultDeclaration")
+  }
+  if (this.tok.type.keyword) {
+    node.declaration = this.parseStatement()
+    node.specifiers = []
+    node.source = null
+  } else {
+    node.declaration = null
+    node.specifiers = this.parseExportSpecifierList()
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null
+    this.semicolon()
+  }
+  return this.finishNode(node, "ExportNamedDeclaration")
+}
+
+lp.parseImport = function() {
+  let node = this.startNode()
+  this.next()
+  if (this.tok.type === tt.string) {
+    node.specifiers = []
+    node.source = this.parseExprAtom()
+    node.kind = ''
+  } else {
+    let elt
+    if (this.tok.type === tt.name && this.tok.value !== "from") {
+      elt = this.startNode()
+      elt.local = this.parseIdent()
+      this.finishNode(elt, "ImportDefaultSpecifier")
+      this.eat(tt.comma)
+    }
+    node.specifiers = this.parseImportSpecifierList()
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : this.dummyString()
+    if (elt) node.specifiers.unshift(elt)
+  }
+  this.semicolon()
+  return this.finishNode(node, "ImportDeclaration")
+}
+
+lp.parseImportSpecifierList = function() {
+  let elts = []
+  if (this.tok.type === tt.star) {
+    let elt = this.startNode()
+    this.next()
+    if (this.eatContextual("as")) elt.local = this.parseIdent()
+    elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"))
+  } else {
+    let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
+    this.pushCx()
+    this.eat(tt.braceL)
+    if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
+    while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
+      let elt = this.startNode()
+      if (this.eat(tt.star)) {
+        if (this.eatContextual("as")) elt.local = this.parseIdent()
+        this.finishNode(elt, "ImportNamespaceSpecifier")
+      } else {
+        if (this.isContextual("from")) break
+        elt.imported = this.parseIdent()
+        if (isDummy(elt.imported)) break
+        elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported
+        this.finishNode(elt, "ImportSpecifier")
+      }
+      elts.push(elt)
+      this.eat(tt.comma)
+    }
+    this.eat(tt.braceR)
+    this.popCx()
+  }
+  return elts
+}
+
+lp.parseExportSpecifierList = function() {
+  let elts = []
+  let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
+  this.pushCx()
+  this.eat(tt.braceL)
+  if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
+  while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
+    if (this.isContextual("from")) break
+    let elt = this.startNode()
+    elt.local = this.parseIdent()
+    if (isDummy(elt.local)) break
+    elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local
+    this.finishNode(elt, "ExportSpecifier")
+    elts.push(elt)
+    this.eat(tt.comma)
+  }
+  this.eat(tt.braceR)
+  this.popCx()
+  return elts
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/tokenize.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/tokenize.js
new file mode 100644
index 0000000..c20db22
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/loose/tokenize.js
@@ -0,0 +1,108 @@
+import {tokTypes as tt, Token, isNewLine, SourceLocation, getLineInfo, lineBreakG} from ".."
+import {LooseParser} from "./state"
+
+const lp = LooseParser.prototype
+
+function isSpace(ch) {
+  return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || isNewLine(ch)
+}
+
+lp.next = function() {
+  this.last = this.tok
+  if (this.ahead.length)
+    this.tok = this.ahead.shift()
+  else
+    this.tok = this.readToken()
+
+  if (this.tok.start >= this.nextLineStart) {
+    while (this.tok.start >= this.nextLineStart) {
+      this.curLineStart = this.nextLineStart
+      this.nextLineStart = this.lineEnd(this.curLineStart) + 1
+    }
+    this.curIndent = this.indentationAfter(this.curLineStart)
+  }
+}
+
+lp.readToken = function() {
+  for (;;) {
+    try {
+      this.toks.next()
+      if (this.toks.type === tt.dot &&
+          this.input.substr(this.toks.end, 1) === "." &&
+          this.options.ecmaVersion >= 6) {
+        this.toks.end++
+        this.toks.type = tt.ellipsis
+      }
+      return new Token(this.toks)
+    } catch(e) {
+      if (!(e instanceof SyntaxError)) throw e
+
+      // Try to skip some text, based on the error message, and then continue
+      let msg = e.message, pos = e.raisedAt, replace = true
+      if (/unterminated/i.test(msg)) {
+        pos = this.lineEnd(e.pos + 1)
+        if (/string/.test(msg)) {
+          replace = {start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos)}
+        } else if (/regular expr/i.test(msg)) {
+          let re = this.input.slice(e.pos, pos)
+          try { re = new RegExp(re) } catch(e) {}
+          replace = {start: e.pos, end: pos, type: tt.regexp, value: re}
+        } else if (/template/.test(msg)) {
+          replace = {start: e.pos, end: pos,
+                     type: tt.template,
+                     value: this.input.slice(e.pos, pos)}
+        } else {
+          replace = false
+        }
+      } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number|expected number in radix/i.test(msg)) {
+        while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos
+      } else if (/character escape|expected hexadecimal/i.test(msg)) {
+        while (pos < this.input.length) {
+          let ch = this.input.charCodeAt(pos++)
+          if (ch === 34 || ch === 39 || isNewLine(ch)) break
+        }
+      } else if (/unexpected character/i.test(msg)) {
+        pos++
+        replace = false
+      } else if (/regular expression/i.test(msg)) {
+        replace = true
+      } else {
+        throw e
+      }
+      this.resetTo(pos)
+      if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"}
+      if (replace) {
+        if (this.options.locations)
+          replace.loc = new SourceLocation(
+            this.toks,
+            getLineInfo(this.input, replace.start),
+            getLineInfo(this.input, replace.end))
+        return replace
+      }
+    }
+  }
+}
+
+lp.resetTo = function(pos) {
+  this.toks.pos = pos
+  let ch = this.input.charAt(pos - 1)
+  this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) ||
+    /[enwfd]/.test(ch) &&
+    /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos))
+
+  if (this.options.locations) {
+    this.toks.curLine = 1
+    this.toks.lineStart = lineBreakG.lastIndex = 0
+    let match
+    while ((match = lineBreakG.exec(this.input)) && match.index < pos) {
+      ++this.toks.curLine
+      this.toks.lineStart = match.index + match[0].length
+    }
+  }
+}
+
+lp.lookAhead = function(n) {
+  while (n > this.ahead.length)
+    this.ahead.push(this.readToken())
+  return this.ahead[n - 1]
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/lval.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/lval.js
new file mode 100644
index 0000000..4cf79b5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/lval.js
@@ -0,0 +1,204 @@
+import {types as tt} from "./tokentype"
+import {Parser} from "./state"
+import {reservedWords} from "./identifier"
+import {has} from "./util"
+
+const pp = Parser.prototype
+
+// Convert existing expression atom to assignable pattern
+// if possible.
+
+pp.toAssignable = function(node, isBinding) {
+  if (this.options.ecmaVersion >= 6 && node) {
+    switch (node.type) {
+    case "Identifier":
+    case "ObjectPattern":
+    case "ArrayPattern":
+    case "AssignmentPattern":
+      break
+
+    case "ObjectExpression":
+      node.type = "ObjectPattern"
+      for (let i = 0; i < node.properties.length; i++) {
+        let prop = node.properties[i]
+        if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter")
+        this.toAssignable(prop.value, isBinding)
+      }
+      break
+
+    case "ArrayExpression":
+      node.type = "ArrayPattern"
+      this.toAssignableList(node.elements, isBinding)
+      break
+
+    case "AssignmentExpression":
+      if (node.operator === "=") {
+        node.type = "AssignmentPattern"
+        delete node.operator
+      } else {
+        this.raise(node.left.end, "Only '=' operator can be used for specifying default value.")
+      }
+      break
+
+    case "ParenthesizedExpression":
+      node.expression = this.toAssignable(node.expression, isBinding)
+      break
+
+    case "MemberExpression":
+      if (!isBinding) break
+
+    default:
+      this.raise(node.start, "Assigning to rvalue")
+    }
+  }
+  return node
+}
+
+// Convert list of expression atoms to binding list.
+
+pp.toAssignableList = function(exprList, isBinding) {
+  let end = exprList.length
+  if (end) {
+    let last = exprList[end - 1]
+    if (last && last.type == "RestElement") {
+      --end
+    } else if (last && last.type == "SpreadElement") {
+      last.type = "RestElement"
+      let arg = last.argument
+      this.toAssignable(arg, isBinding)
+      if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern")
+        this.unexpected(arg.start)
+      --end
+    }
+  }
+  for (let i = 0; i < end; i++) {
+    let elt = exprList[i]
+    if (elt) this.toAssignable(elt, isBinding)
+  }
+  return exprList
+}
+
+// Parses spread element.
+
+pp.parseSpread = function(refShorthandDefaultPos) {
+  let node = this.startNode()
+  this.next()
+  node.argument = this.parseMaybeAssign(refShorthandDefaultPos)
+  return this.finishNode(node, "SpreadElement")
+}
+
+pp.parseRest = function() {
+  let node = this.startNode()
+  this.next()
+  node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected()
+  return this.finishNode(node, "RestElement")
+}
+
+// Parses lvalue (assignable) atom.
+
+pp.parseBindingAtom = function() {
+  if (this.options.ecmaVersion < 6) return this.parseIdent()
+  switch (this.type) {
+  case tt.name:
+    return this.parseIdent()
+
+  case tt.bracketL:
+    let node = this.startNode()
+    this.next()
+    node.elements = this.parseBindingList(tt.bracketR, true, true)
+    return this.finishNode(node, "ArrayPattern")
+
+  case tt.braceL:
+    return this.parseObj(true)
+
+  default:
+    this.unexpected()
+  }
+}
+
+pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
+  let elts = [], first = true
+  while (!this.eat(close)) {
+    if (first) first = false
+    else this.expect(tt.comma)
+    if (allowEmpty && this.type === tt.comma) {
+      elts.push(null)
+    } else if (allowTrailingComma && this.afterTrailingComma(close)) {
+      break
+    } else if (this.type === tt.ellipsis) {
+      let rest = this.parseRest()
+      this.parseBindingListItem(rest)
+      elts.push(rest)
+      this.expect(close)
+      break
+    } else {
+      let elem = this.parseMaybeDefault(this.start, this.startLoc)
+      this.parseBindingListItem(elem)
+      elts.push(elem)
+    }
+  }
+  return elts
+}
+
+pp.parseBindingListItem = function(param) {
+  return param
+}
+
+// Parses assignment pattern around given atom if possible.
+
+pp.parseMaybeDefault = function(startPos, startLoc, left) {
+  left = left || this.parseBindingAtom()
+  if (this.options.ecmaVersion < 6 || !this.eat(tt.eq)) return left
+  let node = this.startNodeAt(startPos, startLoc)
+  node.left = left
+  node.right = this.parseMaybeAssign()
+  return this.finishNode(node, "AssignmentPattern")
+}
+
+// Verify that a node is an lval — something that can be assigned
+// to.
+
+pp.checkLVal = function(expr, isBinding, checkClashes) {
+  switch (expr.type) {
+  case "Identifier":
+    if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name)))
+      this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode")
+    if (checkClashes) {
+      if (has(checkClashes, expr.name))
+        this.raise(expr.start, "Argument name clash in strict mode")
+      checkClashes[expr.name] = true
+    }
+    break
+
+  case "MemberExpression":
+    if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression")
+    break
+
+  case "ObjectPattern":
+    for (let i = 0; i < expr.properties.length; i++)
+      this.checkLVal(expr.properties[i].value, isBinding, checkClashes)
+    break
+
+  case "ArrayPattern":
+    for (let i = 0; i < expr.elements.length; i++) {
+      let elem = expr.elements[i]
+      if (elem) this.checkLVal(elem, isBinding, checkClashes)
+    }
+    break
+
+  case "AssignmentPattern":
+    this.checkLVal(expr.left, isBinding, checkClashes)
+    break
+
+  case "RestElement":
+    this.checkLVal(expr.argument, isBinding, checkClashes)
+    break
+
+  case "ParenthesizedExpression":
+    this.checkLVal(expr.expression, isBinding, checkClashes)
+    break
+
+  default:
+    this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue")
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/node.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/node.js
new file mode 100644
index 0000000..76b5b09
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/node.js
@@ -0,0 +1,50 @@
+import {Parser} from "./state"
+import {SourceLocation} from "./locutil"
+
+export class Node {
+  constructor(parser, pos, loc) {
+    this.type = ""
+    this.start = pos
+    this.end = 0
+    if (parser.options.locations)
+      this.loc = new SourceLocation(parser, loc)
+    if (parser.options.directSourceFile)
+      this.sourceFile = parser.options.directSourceFile
+    if (parser.options.ranges)
+      this.range = [pos, 0]
+  }
+}
+
+// Start an AST node, attaching a start offset.
+
+const pp = Parser.prototype
+
+pp.startNode = function() {
+  return new Node(this, this.start, this.startLoc)
+}
+
+pp.startNodeAt = function(pos, loc) {
+  return new Node(this, pos, loc)
+}
+
+// Finish an AST node, adding `type` and `end` properties.
+
+function finishNodeAt(node, type, pos, loc) {
+  node.type = type
+  node.end = pos
+  if (this.options.locations)
+    node.loc.end = loc
+  if (this.options.ranges)
+    node.range[1] = pos
+  return node
+}
+
+pp.finishNode = function(node, type) {
+  return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)
+}
+
+// Finish node at given position
+
+pp.finishNodeAt = function(node, type, pos, loc) {
+  return finishNodeAt.call(this, node, type, pos, loc)
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/options.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/options.js
new file mode 100644
index 0000000..7903014
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/options.js
@@ -0,0 +1,119 @@
+import {has, isArray} from "./util"
+import {SourceLocation} from "./locutil"
+
+// A second optional argument can be given to further configure
+// the parser process. These options are recognized:
+
+export const defaultOptions = {
+  // `ecmaVersion` indicates the ECMAScript version to parse. Must
+  // be either 3, or 5, or 6. This influences support for strict
+  // mode, the set of reserved words, support for getters and
+  // setters and other features.
+  ecmaVersion: 5,
+  // Source type ("script" or "module") for different semantics
+  sourceType: "script",
+  // `onInsertedSemicolon` can be a callback that will be called
+  // when a semicolon is automatically inserted. It will be passed
+  // th position of the comma as an offset, and if `locations` is
+  // enabled, it is given the location as a `{line, column}` object
+  // as second argument.
+  onInsertedSemicolon: null,
+  // `onTrailingComma` is similar to `onInsertedSemicolon`, but for
+  // trailing commas.
+  onTrailingComma: null,
+  // By default, reserved words are not enforced. Disable
+  // `allowReserved` to enforce them. When this option has the
+  // value "never", reserved words and keywords can also not be
+  // used as property names.
+  allowReserved: true,
+  // When enabled, a return at the top level is not considered an
+  // error.
+  allowReturnOutsideFunction: false,
+  // When enabled, import/export statements are not constrained to
+  // appearing at the top of the program.
+  allowImportExportEverywhere: false,
+  // When enabled, hashbang directive in the beginning of file
+  // is allowed and treated as a line comment.
+  allowHashBang: false,
+  // When `locations` is on, `loc` properties holding objects with
+  // `start` and `end` properties in `{line, column}` form (with
+  // line being 1-based and column 0-based) will be attached to the
+  // nodes.
+  locations: false,
+  // A function can be passed as `onToken` option, which will
+  // cause Acorn to call that function with object in the same
+  // format as tokenize() returns. Note that you are not
+  // allowed to call the parser from the callback—that will
+  // corrupt its internal state.
+  onToken: null,
+  // A function can be passed as `onComment` option, which will
+  // cause Acorn to call that function with `(block, text, start,
+  // end)` parameters whenever a comment is skipped. `block` is a
+  // boolean indicating whether this is a block (`/* */`) comment,
+  // `text` is the content of the comment, and `start` and `end` are
+  // character offsets that denote the start and end of the comment.
+  // When the `locations` option is on, two more parameters are
+  // passed, the full `{line, column}` locations of the start and
+  // end of the comments. Note that you are not allowed to call the
+  // parser from the callback—that will corrupt its internal state.
+  onComment: null,
+  // Nodes have their start and end characters offsets recorded in
+  // `start` and `end` properties (directly on the node, rather than
+  // the `loc` object, which holds line/column data. To also add a
+  // [semi-standardized][range] `range` property holding a `[start,
+  // end]` array with the same numbers, set the `ranges` option to
+  // `true`.
+  //
+  // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
+  ranges: false,
+  // It is possible to parse multiple files into a single AST by
+  // passing the tree produced by parsing the first file as
+  // `program` option in subsequent parses. This will add the
+  // toplevel forms of the parsed file to the `Program` (top) node
+  // of an existing parse tree.
+  program: null,
+  // When `locations` is on, you can pass this to record the source
+  // file in every node's `loc` object.
+  sourceFile: null,
+  // This value, if given, is stored in every node, whether
+  // `locations` is on or off.
+  directSourceFile: null,
+  // When enabled, parenthesized expressions are represented by
+  // (non-standard) ParenthesizedExpression nodes
+  preserveParens: false,
+  plugins: {}
+}
+
+// Interpret and default an options object
+
+export function getOptions(opts) {
+  let options = {}
+  for (let opt in defaultOptions)
+    options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]
+
+  if (isArray(options.onToken)) {
+    let tokens = options.onToken
+    options.onToken = (token) => tokens.push(token)
+  }
+  if (isArray(options.onComment))
+    options.onComment = pushComment(options, options.onComment)
+
+  return options
+}
+
+function pushComment(options, array) {
+  return function (block, text, start, end, startLoc, endLoc) {
+    let comment = {
+      type: block ? 'Block' : 'Line',
+      value: text,
+      start: start,
+      end: end
+    }
+    if (options.locations)
+      comment.loc = new SourceLocation(this, startLoc, endLoc)
+    if (options.ranges)
+      comment.range = [start, end]
+    array.push(comment)
+  }
+}
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/parseutil.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/parseutil.js
new file mode 100644
index 0000000..8a4a2cf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/parseutil.js
@@ -0,0 +1,90 @@
+import {types as tt} from "./tokentype"
+import {Parser} from "./state"
+import {lineBreak} from "./whitespace"
+
+const pp = Parser.prototype
+
+// ## Parser utilities
+
+// Test whether a statement node is the string literal `"use strict"`.
+
+pp.isUseStrict = function(stmt) {
+  return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
+    stmt.expression.type === "Literal" &&
+    stmt.expression.raw.slice(1, -1) === "use strict"
+}
+
+// Predicate that tests whether the next token is of the given
+// type, and if yes, consumes it as a side effect.
+
+pp.eat = function(type) {
+  if (this.type === type) {
+    this.next()
+    return true
+  } else {
+    return false
+  }
+}
+
+// Tests whether parsed token is a contextual keyword.
+
+pp.isContextual = function(name) {
+  return this.type === tt.name && this.value === name
+}
+
+// Consumes contextual keyword if possible.
+
+pp.eatContextual = function(name) {
+  return this.value === name && this.eat(tt.name)
+}
+
+// Asserts that following token is given contextual keyword.
+
+pp.expectContextual = function(name) {
+  if (!this.eatContextual(name)) this.unexpected()
+}
+
+// Test whether a semicolon can be inserted at the current position.
+
+pp.canInsertSemicolon = function() {
+  return this.type === tt.eof ||
+    this.type === tt.braceR ||
+    lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
+}
+
+pp.insertSemicolon = function() {
+  if (this.canInsertSemicolon()) {
+    if (this.options.onInsertedSemicolon)
+      this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc)
+    return true
+  }
+}
+
+// Consume a semicolon, or, failing that, see if we are allowed to
+// pretend that there is a semicolon at this position.
+
+pp.semicolon = function() {
+  if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected()
+}
+
+pp.afterTrailingComma = function(tokType) {
+  if (this.type == tokType) {
+    if (this.options.onTrailingComma)
+      this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc)
+    this.next()
+    return true
+  }
+}
+
+// Expect a token of a given type. If found, consume it, otherwise,
+// raise an unexpected token error.
+
+pp.expect = function(type) {
+  this.eat(type) || this.unexpected()
+}
+
+// Raise an unexpected token error.
+
+pp.unexpected = function(pos) {
+  this.raise(pos != null ? pos : this.start, "Unexpected token")
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/state.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/state.js
new file mode 100644
index 0000000..542da9e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/state.js
@@ -0,0 +1,91 @@
+import {reservedWords, keywords} from "./identifier"
+import {types as tt} from "./tokentype"
+import {lineBreak} from "./whitespace"
+import {getOptions} from "./options"
+
+// Registered plugins
+export const plugins = {}
+
+export class Parser {
+  constructor(options, input, startPos) {
+    this.options = getOptions(options)
+    this.sourceFile = this.options.sourceFile
+    this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5]
+    this.isReservedWord = reservedWords[this.options.ecmaVersion]
+    this.input = String(input)
+
+    // Used to signal to callers of `readWord1` whether the word
+    // contained any escape sequences. This is needed because words with
+    // escape sequences must not be interpreted as keywords.
+    this.containsEsc = false;
+
+    // Load plugins
+    this.loadPlugins(this.options.plugins)
+
+    // Set up token state
+
+    // The current position of the tokenizer in the input.
+    if (startPos) {
+      this.pos = startPos
+      this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos))
+      this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
+    } else {
+      this.pos = this.lineStart = 0
+      this.curLine = 1
+    }
+
+    // Properties of the current token:
+    // Its type
+    this.type = tt.eof
+    // For tokens that include more information than their type, the value
+    this.value = null
+    // Its start and end offset
+    this.start = this.end = this.pos
+    // And, if locations are used, the {line, column} object
+    // corresponding to those offsets
+    this.startLoc = this.endLoc = this.curPosition()
+
+    // Position information for the previous token
+    this.lastTokEndLoc = this.lastTokStartLoc = null
+    this.lastTokStart = this.lastTokEnd = this.pos
+
+    // The context stack is used to superficially track syntactic
+    // context to predict whether a regular expression is allowed in a
+    // given position.
+    this.context = this.initialContext()
+    this.exprAllowed = true
+
+    // Figure out if it's a module code.
+    this.strict = this.inModule = this.options.sourceType === "module"
+
+    // Used to signify the start of a potential arrow function
+    this.potentialArrowAt = -1
+
+    // Flags to track whether we are in a function, a generator.
+    this.inFunction = this.inGenerator = false
+    // Labels in scope.
+    this.labels = []
+
+    // If enabled, skip leading hashbang line.
+    if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === '#!')
+      this.skipLineComment(2)
+  }
+
+  extend(name, f) {
+    this[name] = f(this[name])
+  }
+
+  loadPlugins(pluginConfigs) {
+    for (let name in pluginConfigs) {
+      let plugin = plugins[name]
+      if (!plugin) throw new Error("Plugin '" + name + "' not found")
+      plugin(this, pluginConfigs[name])
+    }
+  }
+
+  parse() {
+    let node = this.options.program || this.startNode()
+    this.nextToken()
+    return this.parseTopLevel(node)
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/statement.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/statement.js
new file mode 100644
index 0000000..a932507
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/statement.js
@@ -0,0 +1,612 @@
+import {types as tt} from "./tokentype"
+import {Parser} from "./state"
+import {lineBreak} from "./whitespace"
+
+const pp = Parser.prototype
+
+// ### Statement parsing
+
+// Parse a program. Initializes the parser, reads any number of
+// statements, and wraps them in a Program node.  Optionally takes a
+// `program` argument.  If present, the statements will be appended
+// to its body instead of creating a new node.
+
+pp.parseTopLevel = function(node) {
+  let first = true
+  if (!node.body) node.body = []
+  while (this.type !== tt.eof) {
+    let stmt = this.parseStatement(true, true)
+    node.body.push(stmt)
+    if (first) {
+      if (this.isUseStrict(stmt)) this.setStrict(true)
+      first = false
+    }
+  }
+  this.next()
+  if (this.options.ecmaVersion >= 6) {
+    node.sourceType = this.options.sourceType
+  }
+  return this.finishNode(node, "Program")
+}
+
+const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
+
+// Parse a single statement.
+//
+// If expecting a statement and finding a slash operator, parse a
+// regular expression literal. This is to handle cases like
+// `if (foo) /blah/.exec(foo)`, where looking at the previous token
+// does not help.
+
+pp.parseStatement = function(declaration, topLevel) {
+  let starttype = this.type, node = this.startNode()
+
+  // Most types of statements are recognized by the keyword they
+  // start with. Many are trivial to parse, some require a bit of
+  // complexity.
+
+  switch (starttype) {
+  case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
+  case tt._debugger: return this.parseDebuggerStatement(node)
+  case tt._do: return this.parseDoStatement(node)
+  case tt._for: return this.parseForStatement(node)
+  case tt._function:
+    if (!declaration && this.options.ecmaVersion >= 6) this.unexpected()
+    return this.parseFunctionStatement(node)
+  case tt._class:
+    if (!declaration) this.unexpected()
+    return this.parseClass(node, true)
+  case tt._if: return this.parseIfStatement(node)
+  case tt._return: return this.parseReturnStatement(node)
+  case tt._switch: return this.parseSwitchStatement(node)
+  case tt._throw: return this.parseThrowStatement(node)
+  case tt._try: return this.parseTryStatement(node)
+  case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var
+  case tt._var: return this.parseVarStatement(node, starttype)
+  case tt._while: return this.parseWhileStatement(node)
+  case tt._with: return this.parseWithStatement(node)
+  case tt.braceL: return this.parseBlock()
+  case tt.semi: return this.parseEmptyStatement(node)
+  case tt._export:
+  case tt._import:
+    if (!this.options.allowImportExportEverywhere) {
+      if (!topLevel)
+        this.raise(this.start, "'import' and 'export' may only appear at the top level")
+      if (!this.inModule)
+        this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")
+    }
+    return starttype === tt._import ? this.parseImport(node) : this.parseExport(node)
+
+    // If the statement does not start with a statement keyword or a
+    // brace, it's an ExpressionStatement or LabeledStatement. We
+    // simply start parsing an expression, and afterwards, if the
+    // next token is a colon and the expression was a simple
+    // Identifier node, we switch to interpreting it as a label.
+  default:
+    let maybeName = this.value, expr = this.parseExpression()
+    if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon))
+      return this.parseLabeledStatement(node, maybeName, expr)
+    else return this.parseExpressionStatement(node, expr)
+  }
+}
+
+pp.parseBreakContinueStatement = function(node, keyword) {
+  let isBreak = keyword == "break"
+  this.next()
+  if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null
+  else if (this.type !== tt.name) this.unexpected()
+  else {
+    node.label = this.parseIdent()
+    this.semicolon()
+  }
+
+  // Verify that there is an actual destination to break or
+  // continue to.
+  for (var i = 0; i < this.labels.length; ++i) {
+    let lab = this.labels[i]
+    if (node.label == null || lab.name === node.label.name) {
+      if (lab.kind != null && (isBreak || lab.kind === "loop")) break
+      if (node.label && isBreak) break
+    }
+  }
+  if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)
+  return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
+}
+
+pp.parseDebuggerStatement = function(node) {
+  this.next()
+  this.semicolon()
+  return this.finishNode(node, "DebuggerStatement")
+}
+
+pp.parseDoStatement = function(node) {
+  this.next()
+  this.labels.push(loopLabel)
+  node.body = this.parseStatement(false)
+  this.labels.pop()
+  this.expect(tt._while)
+  node.test = this.parseParenExpression()
+  if (this.options.ecmaVersion >= 6)
+    this.eat(tt.semi)
+  else
+    this.semicolon()
+  return this.finishNode(node, "DoWhileStatement")
+}
+
+// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
+// loop is non-trivial. Basically, we have to parse the init `var`
+// statement or expression, disallowing the `in` operator (see
+// the second parameter to `parseExpression`), and then check
+// whether the next token is `in` or `of`. When there is no init
+// part (semicolon immediately after the opening parenthesis), it
+// is a regular `for` loop.
+
+pp.parseForStatement = function(node) {
+  this.next()
+  this.labels.push(loopLabel)
+  this.expect(tt.parenL)
+  if (this.type === tt.semi) return this.parseFor(node, null)
+  if (this.type === tt._var || this.type === tt._let || this.type === tt._const) {
+    let init = this.startNode(), varKind = this.type
+    this.next()
+    this.parseVar(init, true, varKind)
+    this.finishNode(init, "VariableDeclaration")
+    if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 &&
+        !(varKind !== tt._var && init.declarations[0].init))
+      return this.parseForIn(node, init)
+    return this.parseFor(node, init)
+  }
+  let refShorthandDefaultPos = {start: 0}
+  let init = this.parseExpression(true, refShorthandDefaultPos)
+  if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
+    this.toAssignable(init)
+    this.checkLVal(init)
+    return this.parseForIn(node, init)
+  } else if (refShorthandDefaultPos.start) {
+    this.unexpected(refShorthandDefaultPos.start)
+  }
+  return this.parseFor(node, init)
+}
+
+pp.parseFunctionStatement = function(node) {
+  this.next()
+  return this.parseFunction(node, true)
+}
+
+pp.parseIfStatement = function(node) {
+  this.next()
+  node.test = this.parseParenExpression()
+  node.consequent = this.parseStatement(false)
+  node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null
+  return this.finishNode(node, "IfStatement")
+}
+
+pp.parseReturnStatement = function(node) {
+  if (!this.inFunction && !this.options.allowReturnOutsideFunction)
+    this.raise(this.start, "'return' outside of function")
+  this.next()
+
+  // In `return` (and `break`/`continue`), the keywords with
+  // optional arguments, we eagerly look for a semicolon or the
+  // possibility to insert one.
+
+  if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null
+  else { node.argument = this.parseExpression(); this.semicolon() }
+  return this.finishNode(node, "ReturnStatement")
+}
+
+pp.parseSwitchStatement = function(node) {
+  this.next()
+  node.discriminant = this.parseParenExpression()
+  node.cases = []
+  this.expect(tt.braceL)
+  this.labels.push(switchLabel)
+
+  // Statements under must be grouped (by label) in SwitchCase
+  // nodes. `cur` is used to keep the node that we are currently
+  // adding statements to.
+
+  for (var cur, sawDefault = false; this.type != tt.braceR;) {
+    if (this.type === tt._case || this.type === tt._default) {
+      let isCase = this.type === tt._case
+      if (cur) this.finishNode(cur, "SwitchCase")
+      node.cases.push(cur = this.startNode())
+      cur.consequent = []
+      this.next()
+      if (isCase) {
+        cur.test = this.parseExpression()
+      } else {
+        if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses")
+        sawDefault = true
+        cur.test = null
+      }
+      this.expect(tt.colon)
+    } else {
+      if (!cur) this.unexpected()
+      cur.consequent.push(this.parseStatement(true))
+    }
+  }
+  if (cur) this.finishNode(cur, "SwitchCase")
+  this.next() // Closing brace
+  this.labels.pop()
+  return this.finishNode(node, "SwitchStatement")
+}
+
+pp.parseThrowStatement = function(node) {
+  this.next()
+  if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
+    this.raise(this.lastTokEnd, "Illegal newline after throw")
+  node.argument = this.parseExpression()
+  this.semicolon()
+  return this.finishNode(node, "ThrowStatement")
+}
+
+// Reused empty array added for node fields that are always empty.
+
+const empty = []
+
+pp.parseTryStatement = function(node) {
+  this.next()
+  node.block = this.parseBlock()
+  node.handler = null
+  if (this.type === tt._catch) {
+    let clause = this.startNode()
+    this.next()
+    this.expect(tt.parenL)
+    clause.param = this.parseBindingAtom()
+    this.checkLVal(clause.param, true)
+    this.expect(tt.parenR)
+    clause.guard = null
+    clause.body = this.parseBlock()
+    node.handler = this.finishNode(clause, "CatchClause")
+  }
+  node.guardedHandlers = empty
+  node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
+  if (!node.handler && !node.finalizer)
+    this.raise(node.start, "Missing catch or finally clause")
+  return this.finishNode(node, "TryStatement")
+}
+
+pp.parseVarStatement = function(node, kind) {
+  this.next()
+  this.parseVar(node, false, kind)
+  this.semicolon()
+  return this.finishNode(node, "VariableDeclaration")
+}
+
+pp.parseWhileStatement = function(node) {
+  this.next()
+  node.test = this.parseParenExpression()
+  this.labels.push(loopLabel)
+  node.body = this.parseStatement(false)
+  this.labels.pop()
+  return this.finishNode(node, "WhileStatement")
+}
+
+pp.parseWithStatement = function(node) {
+  if (this.strict) this.raise(this.start, "'with' in strict mode")
+  this.next()
+  node.object = this.parseParenExpression()
+  node.body = this.parseStatement(false)
+  return this.finishNode(node, "WithStatement")
+}
+
+pp.parseEmptyStatement = function(node) {
+  this.next()
+  return this.finishNode(node, "EmptyStatement")
+}
+
+pp.parseLabeledStatement = function(node, maybeName, expr) {
+  for (let i = 0; i < this.labels.length; ++i)
+    if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared")
+  let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null
+  for (let i = this.labels.length - 1; i >= 0; i--) {
+    let label = this.labels[i]
+    if (label.statementStart == node.start) {
+      label.statementStart = this.start;
+      label.kind = kind;
+    } else break;
+  }
+  this.labels.push({name: maybeName, kind: kind, statementStart: this.start})
+  node.body = this.parseStatement(true)
+  this.labels.pop()
+  node.label = expr
+  return this.finishNode(node, "LabeledStatement")
+}
+
+pp.parseExpressionStatement = function(node, expr) {
+  node.expression = expr
+  this.semicolon()
+  return this.finishNode(node, "ExpressionStatement")
+}
+
+// Parse a semicolon-enclosed block of statements, handling `"use
+// strict"` declarations when `allowStrict` is true (used for
+// function bodies).
+
+pp.parseBlock = function(allowStrict) {
+  let node = this.startNode(), first = true, oldStrict
+  node.body = []
+  this.expect(tt.braceL)
+  while (!this.eat(tt.braceR)) {
+    let stmt = this.parseStatement(true)
+    node.body.push(stmt)
+    if (first && allowStrict && this.isUseStrict(stmt)) {
+      oldStrict = this.strict
+      this.setStrict(this.strict = true)
+    }
+    first = false
+  }
+  if (oldStrict === false) this.setStrict(false)
+  return this.finishNode(node, "BlockStatement")
+}
+
+// Parse a regular `for` loop. The disambiguation code in
+// `parseStatement` will already have parsed the init statement or
+// expression.
+
+pp.parseFor = function(node, init) {
+  node.init = init
+  this.expect(tt.semi)
+  node.test = this.type === tt.semi ? null : this.parseExpression()
+  this.expect(tt.semi)
+  node.update = this.type === tt.parenR ? null : this.parseExpression()
+  this.expect(tt.parenR)
+  node.body = this.parseStatement(false)
+  this.labels.pop()
+  return this.finishNode(node, "ForStatement")
+}
+
+// Parse a `for`/`in` and `for`/`of` loop, which are almost
+// same from parser's perspective.
+
+pp.parseForIn = function(node, init) {
+  let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"
+  this.next()
+  node.left = init
+  node.right = this.parseExpression()
+  this.expect(tt.parenR)
+  node.body = this.parseStatement(false)
+  this.labels.pop()
+  return this.finishNode(node, type)
+}
+
+// Parse a list of variable declarations.
+
+pp.parseVar = function(node, isFor, kind) {
+  node.declarations = []
+  node.kind = kind.keyword
+  for (;;) {
+    let decl = this.startNode()
+    this.parseVarId(decl)
+    if (this.eat(tt.eq)) {
+      decl.init = this.parseMaybeAssign(isFor)
+    } else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {
+      this.unexpected()
+    } else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) {
+      this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value")
+    } else {
+      decl.init = null
+    }
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
+    if (!this.eat(tt.comma)) break
+  }
+  return node
+}
+
+pp.parseVarId = function(decl) {
+  decl.id = this.parseBindingAtom()
+  this.checkLVal(decl.id, true)
+}
+
+// Parse a function declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseFunction = function(node, isStatement, allowExpressionBody) {
+  this.initFunction(node)
+  if (this.options.ecmaVersion >= 6)
+    node.generator = this.eat(tt.star)
+  if (isStatement || this.type === tt.name)
+    node.id = this.parseIdent()
+  this.parseFunctionParams(node)
+  this.parseFunctionBody(node, allowExpressionBody)
+  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
+}
+
+pp.parseFunctionParams = function(node) {
+  this.expect(tt.parenL)
+  node.params = this.parseBindingList(tt.parenR, false, false)
+}
+
+// Parse a class declaration or literal (depending on the
+// `isStatement` parameter).
+
+pp.parseClass = function(node, isStatement) {
+  this.next()
+  this.parseClassId(node, isStatement)
+  this.parseClassSuper(node)
+  let classBody = this.startNode()
+  let hadConstructor = false
+  classBody.body = []
+  this.expect(tt.braceL)
+  while (!this.eat(tt.braceR)) {
+    if (this.eat(tt.semi)) continue
+    let method = this.startNode()
+    let isGenerator = this.eat(tt.star)
+    let isMaybeStatic = this.type === tt.name && this.value === "static"
+    this.parsePropertyName(method)
+    method.static = isMaybeStatic && this.type !== tt.parenL
+    if (method.static) {
+      if (isGenerator) this.unexpected()
+      isGenerator = this.eat(tt.star)
+      this.parsePropertyName(method)
+    }
+    method.kind = "method"
+    let isGetSet = false
+    if (!method.computed) {
+      let {key} = method
+      if (!isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) {
+        isGetSet = true
+        method.kind = key.name
+        key = this.parsePropertyName(method)
+      }
+      if (!method.static && (key.type === "Identifier" && key.name === "constructor" ||
+          key.type === "Literal" && key.value === "constructor")) {
+        if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class")
+        if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier")
+        if (isGenerator) this.raise(key.start, "Constructor can't be a generator")
+        method.kind = "constructor"
+        hadConstructor = true
+      }
+    }
+    this.parseClassMethod(classBody, method, isGenerator)
+    if (isGetSet) {
+      let paramCount = method.kind === "get" ? 0 : 1
+      if (method.value.params.length !== paramCount) {
+        let start = method.value.start
+        if (method.kind === "get")
+          this.raise(start, "getter should have no params");
+        else
+          this.raise(start, "setter should have exactly one param")
+      }
+    }
+  }
+  node.body = this.finishNode(classBody, "ClassBody")
+  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
+}
+
+pp.parseClassMethod = function(classBody, method, isGenerator) {
+  method.value = this.parseMethod(isGenerator)
+  classBody.body.push(this.finishNode(method, "MethodDefinition"))
+}
+
+pp.parseClassId = function(node, isStatement) {
+  node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null
+}
+
+pp.parseClassSuper = function(node) {
+  node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null
+}
+
+// Parses module export declaration.
+
+pp.parseExport = function(node) {
+  this.next()
+  // export * from '...'
+  if (this.eat(tt.star)) {
+    this.expectContextual("from")
+    node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
+    this.semicolon()
+    return this.finishNode(node, "ExportAllDeclaration")
+  }
+  if (this.eat(tt._default)) { // export default ...
+    let expr = this.parseMaybeAssign()
+    let needsSemi = true
+    if (expr.type == "FunctionExpression" ||
+        expr.type == "ClassExpression") {
+      needsSemi = false
+      if (expr.id) {
+        expr.type = expr.type == "FunctionExpression"
+          ? "FunctionDeclaration"
+          : "ClassDeclaration"
+      }
+    }
+    node.declaration = expr
+    if (needsSemi) this.semicolon()
+    return this.finishNode(node, "ExportDefaultDeclaration")
+  }
+  // export var|const|let|function|class ...
+  if (this.shouldParseExportStatement()) {
+    node.declaration = this.parseStatement(true)
+    node.specifiers = []
+    node.source = null
+  } else { // export { x, y as z } [from '...']
+    node.declaration = null
+    node.specifiers = this.parseExportSpecifiers()
+    if (this.eatContextual("from")) {
+      node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
+    } else {
+      node.source = null
+    }
+    this.semicolon()
+  }
+  return this.finishNode(node, "ExportNamedDeclaration")
+}
+
+pp.shouldParseExportStatement = function() {
+  return this.type.keyword
+}
+
+// Parses a comma-separated list of module exports.
+
+pp.parseExportSpecifiers = function() {
+  let nodes = [], first = true
+  // export { x, y as z } [from '...']
+  this.expect(tt.braceL)
+  while (!this.eat(tt.braceR)) {
+    if (!first) {
+      this.expect(tt.comma)
+      if (this.afterTrailingComma(tt.braceR)) break
+    } else first = false
+
+    let node = this.startNode()
+    node.local = this.parseIdent(this.type === tt._default)
+    node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local
+    nodes.push(this.finishNode(node, "ExportSpecifier"))
+  }
+  return nodes
+}
+
+// Parses import declaration.
+
+pp.parseImport = function(node) {
+  this.next()
+  // import '...'
+  if (this.type === tt.string) {
+    node.specifiers = empty
+    node.source = this.parseExprAtom()
+  } else {
+    node.specifiers = this.parseImportSpecifiers()
+    this.expectContextual("from")
+    node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
+  }
+  this.semicolon()
+  return this.finishNode(node, "ImportDeclaration")
+}
+
+// Parses a comma-separated list of module imports.
+
+pp.parseImportSpecifiers = function() {
+  let nodes = [], first = true
+  if (this.type === tt.name) {
+    // import defaultObj, { x, y as z } from '...'
+    let node = this.startNode()
+    node.local = this.parseIdent()
+    this.checkLVal(node.local, true)
+    nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))
+    if (!this.eat(tt.comma)) return nodes
+  }
+  if (this.type === tt.star) {
+    let node = this.startNode()
+    this.next()
+    this.expectContextual("as")
+    node.local = this.parseIdent()
+    this.checkLVal(node.local, true)
+    nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
+    return nodes
+  }
+  this.expect(tt.braceL)
+  while (!this.eat(tt.braceR)) {
+    if (!first) {
+      this.expect(tt.comma)
+      if (this.afterTrailingComma(tt.braceR)) break
+    } else first = false
+
+    let node = this.startNode()
+    node.imported = this.parseIdent(true)
+    node.local = this.eatContextual("as") ? this.parseIdent() : node.imported
+    this.checkLVal(node.local, true)
+    nodes.push(this.finishNode(node, "ImportSpecifier"))
+  }
+  return nodes
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokencontext.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokencontext.js
new file mode 100644
index 0000000..c76fc9d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokencontext.js
@@ -0,0 +1,109 @@
+// The algorithm used to determine whether a regexp can appear at a
+// given point in the program is loosely based on sweet.js' approach.
+// See https://github.com/mozilla/sweet.js/wiki/design
+
+import {Parser} from "./state"
+import {types as tt} from "./tokentype"
+import {lineBreak} from "./whitespace"
+
+export class TokContext {
+  constructor(token, isExpr, preserveSpace, override) {
+    this.token = token
+    this.isExpr = !!isExpr
+    this.preserveSpace = !!preserveSpace
+    this.override = override
+  }
+}
+
+export const types = {
+  b_stat: new TokContext("{", false),
+  b_expr: new TokContext("{", true),
+  b_tmpl: new TokContext("${", true),
+  p_stat: new TokContext("(", false),
+  p_expr: new TokContext("(", true),
+  q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()),
+  f_expr: new TokContext("function", true)
+}
+
+const pp = Parser.prototype
+
+pp.initialContext = function() {
+  return [types.b_stat]
+}
+
+pp.braceIsBlock = function(prevType) {
+  if (prevType === tt.colon) {
+    let parent = this.curContext()
+    if (parent === types.b_stat || parent === types.b_expr)
+      return !parent.isExpr
+  }
+  if (prevType === tt._return)
+    return lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
+  if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof || prevType === tt.parenR)
+    return true
+  if (prevType == tt.braceL)
+    return this.curContext() === types.b_stat
+  return !this.exprAllowed
+}
+
+pp.updateContext = function(prevType) {
+  let update, type = this.type
+  if (type.keyword && prevType == tt.dot)
+    this.exprAllowed = false
+  else if (update = type.updateContext)
+    update.call(this, prevType)
+  else
+    this.exprAllowed = type.beforeExpr
+}
+
+// Token-specific context update code
+
+tt.parenR.updateContext = tt.braceR.updateContext = function() {
+  if (this.context.length == 1) {
+    this.exprAllowed = true
+    return
+  }
+  let out = this.context.pop()
+  if (out === types.b_stat && this.curContext() === types.f_expr) {
+    this.context.pop()
+    this.exprAllowed = false
+  } else if (out === types.b_tmpl) {
+    this.exprAllowed = true
+  } else {
+    this.exprAllowed = !out.isExpr
+  }
+}
+
+tt.braceL.updateContext = function(prevType) {
+  this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr)
+  this.exprAllowed = true
+}
+
+tt.dollarBraceL.updateContext = function() {
+  this.context.push(types.b_tmpl)
+  this.exprAllowed = true
+}
+
+tt.parenL.updateContext = function(prevType) {
+  let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while
+  this.context.push(statementParens ? types.p_stat : types.p_expr)
+  this.exprAllowed = true
+}
+
+tt.incDec.updateContext = function() {
+  // tokExprAllowed stays unchanged
+}
+
+tt._function.updateContext = function() {
+  if (this.curContext() !== types.b_stat)
+    this.context.push(types.f_expr)
+  this.exprAllowed = false
+}
+
+tt.backQuote.updateContext = function() {
+  if (this.curContext() === types.q_tmpl)
+    this.context.pop()
+  else
+    this.context.push(types.q_tmpl)
+  this.exprAllowed = false
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokenize.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokenize.js
new file mode 100644
index 0000000..0409f88
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokenize.js
@@ -0,0 +1,682 @@
+import {isIdentifierStart, isIdentifierChar} from "./identifier"
+import {types as tt, keywords as keywordTypes} from "./tokentype"
+import {Parser} from "./state"
+import {SourceLocation} from "./locutil"
+import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace"
+
+// Object type used to represent tokens. Note that normally, tokens
+// simply exist as properties on the parser object. This is only
+// used for the onToken callback and the external tokenizer.
+
+export class Token {
+  constructor(p) {
+    this.type = p.type
+    this.value = p.value
+    this.start = p.start
+    this.end = p.end
+    if (p.options.locations)
+      this.loc = new SourceLocation(p, p.startLoc, p.endLoc)
+    if (p.options.ranges)
+      this.range = [p.start, p.end]
+  }
+}
+
+// ## Tokenizer
+
+const pp = Parser.prototype
+
+// Are we running under Rhino?
+const isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]"
+
+// Move to the next token
+
+pp.next = function() {
+  if (this.options.onToken)
+    this.options.onToken(new Token(this))
+
+  this.lastTokEnd = this.end
+  this.lastTokStart = this.start
+  this.lastTokEndLoc = this.endLoc
+  this.lastTokStartLoc = this.startLoc
+  this.nextToken()
+}
+
+pp.getToken = function() {
+  this.next()
+  return new Token(this)
+}
+
+// If we're in an ES6 environment, make parsers iterable
+if (typeof Symbol !== "undefined")
+  pp[Symbol.iterator] = function () {
+    let self = this
+    return {next: function () {
+      let token = self.getToken()
+      return {
+        done: token.type === tt.eof,
+        value: token
+      }
+    }}
+  }
+
+// Toggle strict mode. Re-reads the next number or string to please
+// pedantic tests (`"use strict"; 010;` should fail).
+
+pp.setStrict = function(strict) {
+  this.strict = strict
+  if (this.type !== tt.num && this.type !== tt.string) return
+  this.pos = this.start
+  if (this.options.locations) {
+    while (this.pos < this.lineStart) {
+      this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1
+      --this.curLine
+    }
+  }
+  this.nextToken()
+}
+
+pp.curContext = function() {
+  return this.context[this.context.length - 1]
+}
+
+// Read a single token, updating the parser object's token-related
+// properties.
+
+pp.nextToken = function() {
+  let curContext = this.curContext()
+  if (!curContext || !curContext.preserveSpace) this.skipSpace()
+
+  this.start = this.pos
+  if (this.options.locations) this.startLoc = this.curPosition()
+  if (this.pos >= this.input.length) return this.finishToken(tt.eof)
+
+  if (curContext.override) return curContext.override(this)
+  else this.readToken(this.fullCharCodeAtPos())
+}
+
+pp.readToken = function(code) {
+  // Identifier or keyword. '\uXXXX' sequences are allowed in
+  // identifiers, so '\' also dispatches to that.
+  if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */)
+    return this.readWord()
+
+  return this.getTokenFromCode(code)
+}
+
+pp.fullCharCodeAtPos = function() {
+  let code = this.input.charCodeAt(this.pos)
+  if (code <= 0xd7ff || code >= 0xe000) return code
+  let next = this.input.charCodeAt(this.pos + 1)
+  return (code << 10) + next - 0x35fdc00
+}
+
+pp.skipBlockComment = function() {
+  let startLoc = this.options.onComment && this.curPosition()
+  let start = this.pos, end = this.input.indexOf("*/", this.pos += 2)
+  if (end === -1) this.raise(this.pos - 2, "Unterminated comment")
+  this.pos = end + 2
+  if (this.options.locations) {
+    lineBreakG.lastIndex = start
+    let match
+    while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
+      ++this.curLine
+      this.lineStart = match.index + match[0].length
+    }
+  }
+  if (this.options.onComment)
+    this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,
+                           startLoc, this.curPosition())
+}
+
+pp.skipLineComment = function(startSkip) {
+  let start = this.pos
+  let startLoc = this.options.onComment && this.curPosition()
+  let ch = this.input.charCodeAt(this.pos+=startSkip)
+  while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
+    ++this.pos
+    ch = this.input.charCodeAt(this.pos)
+  }
+  if (this.options.onComment)
+    this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
+                           startLoc, this.curPosition())
+}
+
+// Called at the start of the parse and after every token. Skips
+// whitespace and comments, and.
+
+pp.skipSpace = function() {
+  loop: while (this.pos < this.input.length) {
+    let ch = this.input.charCodeAt(this.pos)
+    switch (ch) {
+      case 32: case 160: // ' '
+        ++this.pos
+        break
+      case 13:
+        if (this.input.charCodeAt(this.pos + 1) === 10) {
+          ++this.pos
+        }
+      case 10: case 8232: case 8233:
+        ++this.pos
+        if (this.options.locations) {
+          ++this.curLine
+          this.lineStart = this.pos
+        }
+        break
+      case 47: // '/'
+        switch (this.input.charCodeAt(this.pos + 1)) {
+          case 42: // '*'
+            this.skipBlockComment()
+            break
+          case 47:
+            this.skipLineComment(2)
+            break
+          default:
+            break loop
+        }
+        break
+      default:
+        if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
+          ++this.pos
+        } else {
+          break loop
+        }
+    }
+  }
+}
+
+// Called at the end of every token. Sets `end`, `val`, and
+// maintains `context` and `exprAllowed`, and skips the space after
+// the token, so that the next one's `start` will point at the
+// right position.
+
+pp.finishToken = function(type, val) {
+  this.end = this.pos
+  if (this.options.locations) this.endLoc = this.curPosition()
+  let prevType = this.type
+  this.type = type
+  this.value = val
+
+  this.updateContext(prevType)
+}
+
+// ### Token reading
+
+// This is the function that is called to fetch the next token. It
+// is somewhat obscure, because it works in character codes rather
+// than characters, and because operator parsing has been inlined
+// into it.
+//
+// All in the name of speed.
+//
+pp.readToken_dot = function() {
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (next >= 48 && next <= 57) return this.readNumber(true)
+  let next2 = this.input.charCodeAt(this.pos + 2)
+  if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
+    this.pos += 3
+    return this.finishToken(tt.ellipsis)
+  } else {
+    ++this.pos
+    return this.finishToken(tt.dot)
+  }
+}
+
+pp.readToken_slash = function() { // '/'
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (this.exprAllowed) {++this.pos; return this.readRegexp();}
+  if (next === 61) return this.finishOp(tt.assign, 2)
+  return this.finishOp(tt.slash, 1)
+}
+
+pp.readToken_mult_modulo = function(code) { // '%*'
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (next === 61) return this.finishOp(tt.assign, 2)
+  return this.finishOp(code === 42 ? tt.star : tt.modulo, 1)
+}
+
+pp.readToken_pipe_amp = function(code) { // '|&'
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2)
+  if (next === 61) return this.finishOp(tt.assign, 2)
+  return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1)
+}
+
+pp.readToken_caret = function() { // '^'
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (next === 61) return this.finishOp(tt.assign, 2)
+  return this.finishOp(tt.bitwiseXOR, 1)
+}
+
+pp.readToken_plus_min = function(code) { // '+-'
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (next === code) {
+    if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 &&
+        lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) {
+      // A `-->` line comment
+      this.skipLineComment(3)
+      this.skipSpace()
+      return this.nextToken()
+    }
+    return this.finishOp(tt.incDec, 2)
+  }
+  if (next === 61) return this.finishOp(tt.assign, 2)
+  return this.finishOp(tt.plusMin, 1)
+}
+
+pp.readToken_lt_gt = function(code) { // '<>'
+  let next = this.input.charCodeAt(this.pos + 1)
+  let size = 1
+  if (next === code) {
+    size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2
+    if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1)
+    return this.finishOp(tt.bitShift, size)
+  }
+  if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 &&
+      this.input.charCodeAt(this.pos + 3) == 45) {
+    if (this.inModule) this.unexpected()
+    // `<!--`, an XML-style comment that should be interpreted as a line comment
+    this.skipLineComment(4)
+    this.skipSpace()
+    return this.nextToken()
+  }
+  if (next === 61)
+    size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2
+  return this.finishOp(tt.relational, size)
+}
+
+pp.readToken_eq_excl = function(code) { // '=!'
+  let next = this.input.charCodeAt(this.pos + 1)
+  if (next === 61) return this.finishOp(tt.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2)
+  if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'
+    this.pos += 2
+    return this.finishToken(tt.arrow)
+  }
+  return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1)
+}
+
+pp.getTokenFromCode = function(code) {
+  switch (code) {
+    // The interpretation of a dot depends on whether it is followed
+    // by a digit or another two dots.
+  case 46: // '.'
+    return this.readToken_dot()
+
+    // Punctuation tokens.
+  case 40: ++this.pos; return this.finishToken(tt.parenL)
+  case 41: ++this.pos; return this.finishToken(tt.parenR)
+  case 59: ++this.pos; return this.finishToken(tt.semi)
+  case 44: ++this.pos; return this.finishToken(tt.comma)
+  case 91: ++this.pos; return this.finishToken(tt.bracketL)
+  case 93: ++this.pos; return this.finishToken(tt.bracketR)
+  case 123: ++this.pos; return this.finishToken(tt.braceL)
+  case 125: ++this.pos; return this.finishToken(tt.braceR)
+  case 58: ++this.pos; return this.finishToken(tt.colon)
+  case 63: ++this.pos; return this.finishToken(tt.question)
+
+  case 96: // '`'
+    if (this.options.ecmaVersion < 6) break
+    ++this.pos
+    return this.finishToken(tt.backQuote)
+
+  case 48: // '0'
+    let next = this.input.charCodeAt(this.pos + 1)
+    if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
+    if (this.options.ecmaVersion >= 6) {
+      if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
+      if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
+    }
+    // Anything else beginning with a digit is an integer, octal
+    // number, or float.
+  case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
+    return this.readNumber(false)
+
+    // Quotes produce strings.
+  case 34: case 39: // '"', "'"
+    return this.readString(code)
+
+    // Operators are parsed inline in tiny state machines. '=' (61) is
+    // often referred to. `finishOp` simply skips the amount of
+    // characters it is given as second argument, and returns a token
+    // of the type given by its first argument.
+
+  case 47: // '/'
+    return this.readToken_slash()
+
+  case 37: case 42: // '%*'
+    return this.readToken_mult_modulo(code)
+
+  case 124: case 38: // '|&'
+    return this.readToken_pipe_amp(code)
+
+  case 94: // '^'
+    return this.readToken_caret()
+
+  case 43: case 45: // '+-'
+    return this.readToken_plus_min(code)
+
+  case 60: case 62: // '<>'
+    return this.readToken_lt_gt(code)
+
+  case 61: case 33: // '=!'
+    return this.readToken_eq_excl(code)
+
+  case 126: // '~'
+    return this.finishOp(tt.prefix, 1)
+  }
+
+  this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'")
+}
+
+pp.finishOp = function(type, size) {
+  let str = this.input.slice(this.pos, this.pos + size)
+  this.pos += size
+  return this.finishToken(type, str)
+}
+
+// Parse a regular expression. Some context-awareness is necessary,
+// since a '/' inside a '[]' set does not end the expression.
+
+function tryCreateRegexp(src, flags, throwErrorAt) {
+  try {
+    return new RegExp(src, flags);
+  } catch (e) {
+    if (throwErrorAt !== undefined) {
+      if (e instanceof SyntaxError) this.raise(throwErrorAt, "Error parsing regular expression: " + e.message)
+      this.raise(e)
+    }
+  }
+}
+
+var regexpUnicodeSupport = !!tryCreateRegexp("\uffff", "u");
+
+pp.readRegexp = function() {
+  let escaped, inClass, start = this.pos
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression")
+    let ch = this.input.charAt(this.pos)
+    if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression")
+    if (!escaped) {
+      if (ch === "[") inClass = true
+      else if (ch === "]" && inClass) inClass = false
+      else if (ch === "/" && !inClass) break
+      escaped = ch === "\\"
+    } else escaped = false
+    ++this.pos
+  }
+  let content = this.input.slice(start, this.pos)
+  ++this.pos
+  // Need to use `readWord1` because '\uXXXX' sequences are allowed
+  // here (don't ask).
+  let mods = this.readWord1()
+  let tmp = content
+  if (mods) {
+    let validFlags = /^[gmsiy]*$/
+    if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/
+    if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag")
+    if (mods.indexOf('u') >= 0 && !regexpUnicodeSupport) {
+      // Replace each astral symbol and every Unicode escape sequence that
+      // possibly represents an astral symbol or a paired surrogate with a
+      // single ASCII symbol to avoid throwing on regular expressions that
+      // are only valid in combination with the `/u` flag.
+      // Note: replacing with the ASCII symbol `x` might cause false
+      // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
+      // perfectly valid pattern that is equivalent to `[a-b]`, but it would
+      // be replaced by `[x-b]` which throws an error.
+      tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, (match, code, offset) => {
+        code = Number("0x" + code)
+        if (code > 0x10FFFF) this.raise(start + offset + 3, "Code point out of bounds")
+        return "x"
+      });
+      tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x")
+    }
+  }
+  // Detect invalid regular expressions.
+  let value = null
+  // Rhino's regular expression parser is flaky and throws uncatchable exceptions,
+  // so don't do detection if we are running under Rhino
+  if (!isRhino) {
+    tryCreateRegexp(tmp, undefined, start);
+    // Get a regular expression object for this pattern-flag pair, or `null` in
+    // case the current environment doesn't support the flags it uses.
+    value = tryCreateRegexp(content, mods)
+  }
+  return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value})
+}
+
+// Read an integer in the given radix. Return null if zero digits
+// were read, the integer value otherwise. When `len` is given, this
+// will return `null` unless the integer has exactly `len` digits.
+
+pp.readInt = function(radix, len) {
+  let start = this.pos, total = 0
+  for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
+    let code = this.input.charCodeAt(this.pos), val
+    if (code >= 97) val = code - 97 + 10; // a
+    else if (code >= 65) val = code - 65 + 10; // A
+    else if (code >= 48 && code <= 57) val = code - 48; // 0-9
+    else val = Infinity
+    if (val >= radix) break
+    ++this.pos
+    total = total * radix + val
+  }
+  if (this.pos === start || len != null && this.pos - start !== len) return null
+
+  return total
+}
+
+pp.readRadixNumber = function(radix) {
+  this.pos += 2; // 0x
+  let val = this.readInt(radix)
+  if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix)
+  if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number")
+  return this.finishToken(tt.num, val)
+}
+
+// Read an integer, octal integer, or floating-point number.
+
+pp.readNumber = function(startsWithDot) {
+  let start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48
+  if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number")
+  let next = this.input.charCodeAt(this.pos)
+  if (next === 46) { // '.'
+    ++this.pos
+    this.readInt(10)
+    isFloat = true
+    next = this.input.charCodeAt(this.pos)
+  }
+  if (next === 69 || next === 101) { // 'eE'
+    next = this.input.charCodeAt(++this.pos)
+    if (next === 43 || next === 45) ++this.pos; // '+-'
+    if (this.readInt(10) === null) this.raise(start, "Invalid number")
+    isFloat = true
+  }
+  if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number")
+
+  let str = this.input.slice(start, this.pos), val
+  if (isFloat) val = parseFloat(str)
+  else if (!octal || str.length === 1) val = parseInt(str, 10)
+  else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number")
+  else val = parseInt(str, 8)
+  return this.finishToken(tt.num, val)
+}
+
+// Read a string value, interpreting backslash-escapes.
+
+pp.readCodePoint = function() {
+  let ch = this.input.charCodeAt(this.pos), code
+
+  if (ch === 123) {
+    if (this.options.ecmaVersion < 6) this.unexpected()
+    let codePos = ++this.pos
+    code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos)
+    ++this.pos
+    if (code > 0x10FFFF) this.raise(codePos, "Code point out of bounds")
+  } else {
+    code = this.readHexChar(4)
+  }
+  return code
+}
+
+function codePointToString(code) {
+  // UTF-16 Decoding
+  if (code <= 0xFFFF) return String.fromCharCode(code)
+  code -= 0x10000
+  return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)
+}
+
+pp.readString = function(quote) {
+  let out = "", chunkStart = ++this.pos
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant")
+    let ch = this.input.charCodeAt(this.pos)
+    if (ch === quote) break
+    if (ch === 92) { // '\'
+      out += this.input.slice(chunkStart, this.pos)
+      out += this.readEscapedChar(false)
+      chunkStart = this.pos
+    } else {
+      if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant")
+      ++this.pos
+    }
+  }
+  out += this.input.slice(chunkStart, this.pos++)
+  return this.finishToken(tt.string, out)
+}
+
+// Reads template string tokens.
+
+pp.readTmplToken = function() {
+  let out = "", chunkStart = this.pos
+  for (;;) {
+    if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template")
+    let ch = this.input.charCodeAt(this.pos)
+    if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { // '`', '${'
+      if (this.pos === this.start && this.type === tt.template) {
+        if (ch === 36) {
+          this.pos += 2
+          return this.finishToken(tt.dollarBraceL)
+        } else {
+          ++this.pos
+          return this.finishToken(tt.backQuote)
+        }
+      }
+      out += this.input.slice(chunkStart, this.pos)
+      return this.finishToken(tt.template, out)
+    }
+    if (ch === 92) { // '\'
+      out += this.input.slice(chunkStart, this.pos)
+      out += this.readEscapedChar(true)
+      chunkStart = this.pos
+    } else if (isNewLine(ch)) {
+      out += this.input.slice(chunkStart, this.pos)
+      ++this.pos
+      switch (ch) {
+        case 13:
+          if (this.input.charCodeAt(this.pos) === 10) ++this.pos;
+        case 10:
+          out += "\n";
+          break;
+        default:
+          out += String.fromCharCode(ch);
+          break;
+      }
+      if (this.options.locations) {
+        ++this.curLine
+        this.lineStart = this.pos
+      }
+      chunkStart = this.pos
+    } else {
+      ++this.pos
+    }
+  }
+}
+
+// Used to read escaped characters
+
+pp.readEscapedChar = function(inTemplate) {
+  let ch = this.input.charCodeAt(++this.pos)
+  ++this.pos
+  switch (ch) {
+  case 110: return "\n"; // 'n' -> '\n'
+  case 114: return "\r"; // 'r' -> '\r'
+  case 120: return String.fromCharCode(this.readHexChar(2)); // 'x'
+  case 117: return codePointToString(this.readCodePoint()); // 'u'
+  case 116: return "\t"; // 't' -> '\t'
+  case 98: return "\b"; // 'b' -> '\b'
+  case 118: return "\u000b"; // 'v' -> '\u000b'
+  case 102: return "\f"; // 'f' -> '\f'
+  case 13: if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
+  case 10: // ' \n'
+    if (this.options.locations) { this.lineStart = this.pos; ++this.curLine }
+    return ""
+  default:
+    if (ch >= 48 && ch <= 55) {
+      let octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0]
+      let octal = parseInt(octalStr, 8)
+      if (octal > 255) {
+        octalStr = octalStr.slice(0, -1)
+        octal = parseInt(octalStr, 8)
+      }
+      if (octal > 0 && (this.strict || inTemplate)) {
+        this.raise(this.pos - 2, "Octal literal in strict mode")
+      }
+      this.pos += octalStr.length - 1
+      return String.fromCharCode(octal)
+    }
+    return String.fromCharCode(ch)
+  }
+}
+
+// Used to read character escape sequences ('\x', '\u', '\U').
+
+pp.readHexChar = function(len) {
+  let codePos = this.pos
+  let n = this.readInt(16, len)
+  if (n === null) this.raise(codePos, "Bad character escape sequence")
+  return n
+}
+
+// Read an identifier, and return it as a string. Sets `this.containsEsc`
+// to whether the word contained a '\u' escape.
+//
+// Incrementally adds only escaped chars, adding other chunks as-is
+// as a micro-optimization.
+
+pp.readWord1 = function() {
+  this.containsEsc = false
+  let word = "", first = true, chunkStart = this.pos
+  let astral = this.options.ecmaVersion >= 6
+  while (this.pos < this.input.length) {
+    let ch = this.fullCharCodeAtPos()
+    if (isIdentifierChar(ch, astral)) {
+      this.pos += ch <= 0xffff ? 1 : 2
+    } else if (ch === 92) { // "\"
+      this.containsEsc = true
+      word += this.input.slice(chunkStart, this.pos)
+      let escStart = this.pos
+      if (this.input.charCodeAt(++this.pos) != 117) // "u"
+        this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX")
+      ++this.pos
+      let esc = this.readCodePoint()
+      if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))
+        this.raise(escStart, "Invalid Unicode escape")
+      word += codePointToString(esc)
+      chunkStart = this.pos
+    } else {
+      break
+    }
+    first = false
+  }
+  return word + this.input.slice(chunkStart, this.pos)
+}
+
+// Read an identifier or keyword token. Will check for reserved
+// words when necessary.
+
+pp.readWord = function() {
+  let word = this.readWord1()
+  let type = tt.name
+  if ((this.options.ecmaVersion >= 6 || !this.containsEsc) && this.isKeyword(word))
+    type = keywordTypes[word]
+  return this.finishToken(type, word)
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokentype.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokentype.js
new file mode 100644
index 0000000..bd09b7c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/tokentype.js
@@ -0,0 +1,142 @@
+// ## Token types
+
+// The assignment of fine-grained, information-carrying type objects
+// allows the tokenizer to store the information it has about a
+// token in a way that is very cheap for the parser to look up.
+
+// All token type variables start with an underscore, to make them
+// easy to recognize.
+
+// The `beforeExpr` property is used to disambiguate between regular
+// expressions and divisions. It is set on all token types that can
+// be followed by an expression (thus, a slash after them would be a
+// regular expression).
+//
+// `isLoop` marks a keyword as starting a loop, which is important
+// to know when parsing a label, in order to allow or disallow
+// continue jumps to that label.
+
+export class TokenType {
+  constructor(label, conf = {}) {
+    this.label = label
+    this.keyword = conf.keyword
+    this.beforeExpr = !!conf.beforeExpr
+    this.startsExpr = !!conf.startsExpr
+    this.isLoop = !!conf.isLoop
+    this.isAssign = !!conf.isAssign
+    this.prefix = !!conf.prefix
+    this.postfix = !!conf.postfix
+    this.binop = conf.binop || null
+    this.updateContext = null
+  }
+}
+
+function binop(name, prec) {
+  return new TokenType(name, {beforeExpr: true, binop: prec})
+}
+const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true}
+
+export const types = {
+  num: new TokenType("num", startsExpr),
+  regexp: new TokenType("regexp", startsExpr),
+  string: new TokenType("string", startsExpr),
+  name: new TokenType("name", startsExpr),
+  eof: new TokenType("eof"),
+
+  // Punctuation token types.
+  bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
+  bracketR: new TokenType("]"),
+  braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
+  braceR: new TokenType("}"),
+  parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
+  parenR: new TokenType(")"),
+  comma: new TokenType(",", beforeExpr),
+  semi: new TokenType(";", beforeExpr),
+  colon: new TokenType(":", beforeExpr),
+  dot: new TokenType("."),
+  question: new TokenType("?", beforeExpr),
+  arrow: new TokenType("=>", beforeExpr),
+  template: new TokenType("template"),
+  ellipsis: new TokenType("...", beforeExpr),
+  backQuote: new TokenType("`", startsExpr),
+  dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),
+
+  // Operators. These carry several kinds of properties to help the
+  // parser use them properly (the presence of these properties is
+  // what categorizes them as operators).
+  //
+  // `binop`, when present, specifies that this operator is a binary
+  // operator, and will refer to its precedence.
+  //
+  // `prefix` and `postfix` mark the operator as a prefix or postfix
+  // unary operator.
+  //
+  // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
+  // binary operators with a very low precedence, that should result
+  // in AssignmentExpression nodes.
+
+  eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
+  assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
+  incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
+  prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}),
+  logicalOR: binop("||", 1),
+  logicalAND: binop("&&", 2),
+  bitwiseOR: binop("|", 3),
+  bitwiseXOR: binop("^", 4),
+  bitwiseAND: binop("&", 5),
+  equality: binop("==/!=", 6),
+  relational: binop("</>", 7),
+  bitShift: binop("<</>>", 8),
+  plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
+  modulo: binop("%", 10),
+  star: binop("*", 10),
+  slash: binop("/", 10)
+}
+
+// Map keyword names to token types.
+
+export const keywords = {}
+
+// Succinct definitions of keyword token types
+function kw(name, options = {}) {
+  options.keyword = name
+  keywords[name] = types["_" + name] = new TokenType(name, options)
+}
+
+kw("break")
+kw("case", beforeExpr)
+kw("catch")
+kw("continue")
+kw("debugger")
+kw("default", beforeExpr)
+kw("do", {isLoop: true})
+kw("else", beforeExpr)
+kw("finally")
+kw("for", {isLoop: true})
+kw("function", startsExpr)
+kw("if")
+kw("return", beforeExpr)
+kw("switch")
+kw("throw", beforeExpr)
+kw("try")
+kw("var")
+kw("let")
+kw("const")
+kw("while", {isLoop: true})
+kw("with")
+kw("new", {beforeExpr: true, startsExpr: true})
+kw("this", startsExpr)
+kw("super", startsExpr)
+kw("class")
+kw("extends", beforeExpr)
+kw("export")
+kw("import")
+kw("yield", {beforeExpr: true, startsExpr: true})
+kw("null", startsExpr)
+kw("true", startsExpr)
+kw("false", startsExpr)
+kw("in", {beforeExpr: true, binop: 7})
+kw("instanceof", {beforeExpr: true, binop: 7})
+kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true})
+kw("void", {beforeExpr: true, prefix: true, startsExpr: true})
+kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/util.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/util.js
new file mode 100644
index 0000000..3517f8d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/util.js
@@ -0,0 +1,9 @@
+export function isArray(obj) {
+  return Object.prototype.toString.call(obj) === "[object Array]"
+}
+
+// Checks if an object has a property.
+
+export function has(obj, propName) {
+  return Object.prototype.hasOwnProperty.call(obj, propName)
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/walk/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/walk/index.js
new file mode 100644
index 0000000..461f61d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/walk/index.js
@@ -0,0 +1,339 @@
+// AST walker module for Mozilla Parser API compatible trees
+
+// A simple walk is one where you simply specify callbacks to be
+// called on specific nodes. The last two arguments are optional. A
+// simple use would be
+//
+//     walk.simple(myTree, {
+//         Expression: function(node) { ... }
+//     });
+//
+// to do something with all expressions. All Parser API node types
+// can be used to identify node types, as well as Expression,
+// Statement, and ScopeBody, which denote categories of nodes.
+//
+// The base argument can be used to pass a custom (recursive)
+// walker, and state can be used to give this walked an initial
+// state.
+
+export function simple(node, visitors, base, state, override) {
+  if (!base) base = exports.base
+  ;(function c(node, st, override) {
+    let type = override || node.type, found = visitors[type]
+    base[type](node, st, c)
+    if (found) found(node, st)
+  })(node, state, override)
+}
+
+// An ancestor walk builds up an array of ancestor nodes (including
+// the current node) and passes them to the callback as the state parameter.
+export function ancestor(node, visitors, base, state) {
+  if (!base) base = exports.base
+  if (!state) state = []
+  ;(function c(node, st, override) {
+    let type = override || node.type, found = visitors[type]
+    if (node != st[st.length - 1]) {
+      st = st.slice()
+      st.push(node)
+    }
+    base[type](node, st, c)
+    if (found) found(node, st)
+  })(node, state)
+}
+
+// A recursive walk is one where your functions override the default
+// walkers. They can modify and replace the state parameter that's
+// threaded through the walk, and can opt how and whether to walk
+// their child nodes (by calling their third argument on these
+// nodes).
+export function recursive(node, state, funcs, base, override) {
+  let visitor = funcs ? exports.make(funcs, base) : base
+  ;(function c(node, st, override) {
+    visitor[override || node.type](node, st, c)
+  })(node, state, override)
+}
+
+function makeTest(test) {
+  if (typeof test == "string")
+    return type => type == test
+  else if (!test)
+    return () => true
+  else
+    return test
+}
+
+class Found {
+  constructor(node, state) { this.node = node; this.state = state }
+}
+
+// Find a node with a given start, end, and type (all are optional,
+// null can be used as wildcard). Returns a {node, state} object, or
+// undefined when it doesn't find a matching node.
+export function findNodeAt(node, start, end, test, base, state) {
+  test = makeTest(test)
+  if (!base) base = exports.base
+  try {
+    ;(function c(node, st, override) {
+      let type = override || node.type
+      if ((start == null || node.start <= start) &&
+          (end == null || node.end >= end))
+        base[type](node, st, c)
+      if ((start == null || node.start == start) &&
+          (end == null || node.end == end) &&
+          test(type, node))
+        throw new Found(node, st)
+    })(node, state)
+  } catch (e) {
+    if (e instanceof Found) return e
+    throw e
+  }
+}
+
+// Find the innermost node of a given type that contains the given
+// position. Interface similar to findNodeAt.
+export function findNodeAround(node, pos, test, base, state) {
+  test = makeTest(test)
+  if (!base) base = exports.base
+  try {
+    ;(function c(node, st, override) {
+      let type = override || node.type
+      if (node.start > pos || node.end < pos) return
+      base[type](node, st, c)
+      if (test(type, node)) throw new Found(node, st)
+    })(node, state)
+  } catch (e) {
+    if (e instanceof Found) return e
+    throw e
+  }
+}
+
+// Find the outermost matching node after a given position.
+export function findNodeAfter(node, pos, test, base, state) {
+  test = makeTest(test)
+  if (!base) base = exports.base
+  try {
+    ;(function c(node, st, override) {
+      if (node.end < pos) return
+      let type = override || node.type
+      if (node.start >= pos && test(type, node)) throw new Found(node, st)
+      base[type](node, st, c)
+    })(node, state)
+  } catch (e) {
+    if (e instanceof Found) return e
+    throw e
+  }
+}
+
+// Find the outermost matching node before a given position.
+export function findNodeBefore(node, pos, test, base, state) {
+  test = makeTest(test)
+  if (!base) base = exports.base
+  let max
+  ;(function c(node, st, override) {
+    if (node.start > pos) return
+    let type = override || node.type
+    if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
+      max = new Found(node, st)
+    base[type](node, st, c)
+  })(node, state)
+  return max
+}
+
+// Used to create a custom walker. Will fill in all missing node
+// type properties with the defaults.
+export function make(funcs, base) {
+  if (!base) base = exports.base
+  let visitor = {}
+  for (var type in base) visitor[type] = base[type]
+  for (var type in funcs) visitor[type] = funcs[type]
+  return visitor
+}
+
+function skipThrough(node, st, c) { c(node, st) }
+function ignore(_node, _st, _c) {}
+
+// Node walkers.
+
+export const base = {}
+
+base.Program = base.BlockStatement = (node, st, c) => {
+  for (let i = 0; i < node.body.length; ++i)
+    c(node.body[i], st, "Statement")
+}
+base.Statement = skipThrough
+base.EmptyStatement = ignore
+base.ExpressionStatement = base.ParenthesizedExpression =
+  (node, st, c) => c(node.expression, st, "Expression")
+base.IfStatement = (node, st, c) => {
+  c(node.test, st, "Expression")
+  c(node.consequent, st, "Statement")
+  if (node.alternate) c(node.alternate, st, "Statement")
+}
+base.LabeledStatement = (node, st, c) => c(node.body, st, "Statement")
+base.BreakStatement = base.ContinueStatement = ignore
+base.WithStatement = (node, st, c) => {
+  c(node.object, st, "Expression")
+  c(node.body, st, "Statement")
+}
+base.SwitchStatement = (node, st, c) => {
+  c(node.discriminant, st, "Expression")
+  for (let i = 0; i < node.cases.length; ++i) {
+    let cs = node.cases[i]
+    if (cs.test) c(cs.test, st, "Expression")
+    for (let j = 0; j < cs.consequent.length; ++j)
+      c(cs.consequent[j], st, "Statement")
+  }
+}
+base.ReturnStatement = base.YieldExpression = (node, st, c) => {
+  if (node.argument) c(node.argument, st, "Expression")
+}
+base.ThrowStatement = base.SpreadElement =
+  (node, st, c) => c(node.argument, st, "Expression")
+base.TryStatement = (node, st, c) => {
+  c(node.block, st, "Statement")
+  if (node.handler) {
+    c(node.handler.param, st, "Pattern")
+    c(node.handler.body, st, "ScopeBody")
+  }
+  if (node.finalizer) c(node.finalizer, st, "Statement")
+}
+base.WhileStatement = base.DoWhileStatement = (node, st, c) => {
+  c(node.test, st, "Expression")
+  c(node.body, st, "Statement")
+}
+base.ForStatement = (node, st, c) => {
+  if (node.init) c(node.init, st, "ForInit")
+  if (node.test) c(node.test, st, "Expression")
+  if (node.update) c(node.update, st, "Expression")
+  c(node.body, st, "Statement")
+}
+base.ForInStatement = base.ForOfStatement = (node, st, c) => {
+  c(node.left, st, "ForInit")
+  c(node.right, st, "Expression")
+  c(node.body, st, "Statement")
+}
+base.ForInit = (node, st, c) => {
+  if (node.type == "VariableDeclaration") c(node, st)
+  else c(node, st, "Expression")
+}
+base.DebuggerStatement = ignore
+
+base.FunctionDeclaration = (node, st, c) => c(node, st, "Function")
+base.VariableDeclaration = (node, st, c) => {
+  for (let i = 0; i < node.declarations.length; ++i)
+    c(node.declarations[i], st)
+}
+base.VariableDeclarator = (node, st, c) => {
+  c(node.id, st, "Pattern")
+  if (node.init) c(node.init, st, "Expression")
+}
+
+base.Function = (node, st, c) => {
+  if (node.id) c(node.id, st, "Pattern")
+  for (let i = 0; i < node.params.length; i++)
+    c(node.params[i], st, "Pattern")
+  c(node.body, st, node.expression ? "ScopeExpression" : "ScopeBody")
+}
+// FIXME drop these node types in next major version
+// (They are awkward, and in ES6 every block can be a scope.)
+base.ScopeBody = (node, st, c) => c(node, st, "Statement")
+base.ScopeExpression = (node, st, c) => c(node, st, "Expression")
+
+base.Pattern = (node, st, c) => {
+  if (node.type == "Identifier")
+    c(node, st, "VariablePattern")
+  else if (node.type == "MemberExpression")
+    c(node, st, "MemberPattern")
+  else
+    c(node, st)
+}
+base.VariablePattern = ignore
+base.MemberPattern = skipThrough
+base.RestElement = (node, st, c) => c(node.argument, st, "Pattern")
+base.ArrayPattern =  (node, st, c) => {
+  for (let i = 0; i < node.elements.length; ++i) {
+    let elt = node.elements[i]
+    if (elt) c(elt, st, "Pattern")
+  }
+}
+base.ObjectPattern = (node, st, c) => {
+  for (let i = 0; i < node.properties.length; ++i)
+    c(node.properties[i].value, st, "Pattern")
+}
+
+base.Expression = skipThrough
+base.ThisExpression = base.Super = base.MetaProperty = ignore
+base.ArrayExpression = (node, st, c) => {
+  for (let i = 0; i < node.elements.length; ++i) {
+    let elt = node.elements[i]
+    if (elt) c(elt, st, "Expression")
+  }
+}
+base.ObjectExpression = (node, st, c) => {
+  for (let i = 0; i < node.properties.length; ++i)
+    c(node.properties[i], st)
+}
+base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration
+base.SequenceExpression = base.TemplateLiteral = (node, st, c) => {
+  for (let i = 0; i < node.expressions.length; ++i)
+    c(node.expressions[i], st, "Expression")
+}
+base.UnaryExpression = base.UpdateExpression = (node, st, c) => {
+  c(node.argument, st, "Expression")
+}
+base.BinaryExpression = base.LogicalExpression = (node, st, c) => {
+  c(node.left, st, "Expression")
+  c(node.right, st, "Expression")
+}
+base.AssignmentExpression = base.AssignmentPattern = (node, st, c) => {
+  c(node.left, st, "Pattern")
+  c(node.right, st, "Expression")
+}
+base.ConditionalExpression = (node, st, c) => {
+  c(node.test, st, "Expression")
+  c(node.consequent, st, "Expression")
+  c(node.alternate, st, "Expression")
+}
+base.NewExpression = base.CallExpression = (node, st, c) => {
+  c(node.callee, st, "Expression")
+  if (node.arguments) for (let i = 0; i < node.arguments.length; ++i)
+    c(node.arguments[i], st, "Expression")
+}
+base.MemberExpression = (node, st, c) => {
+  c(node.object, st, "Expression")
+  if (node.computed) c(node.property, st, "Expression")
+}
+base.ExportNamedDeclaration = base.ExportDefaultDeclaration = (node, st, c) => {
+  if (node.declaration) c(node.declaration, st)
+  if (node.source) c(node.source, st, "Expression")
+}
+base.ExportAllDeclaration = (node, st, c) => {
+  c(node.source, st, "Expression")
+}
+base.ImportDeclaration = (node, st, c) => {
+  for (let i = 0; i < node.specifiers.length; i++)
+    c(node.specifiers[i], st)
+  c(node.source, st, "Expression")
+}
+base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore
+
+base.TaggedTemplateExpression = (node, st, c) => {
+  c(node.tag, st, "Expression")
+  c(node.quasi, st)
+}
+base.ClassDeclaration = base.ClassExpression = (node, st, c) => c(node, st, "Class")
+base.Class = (node, st, c) => {
+  if (node.id) c(node.id, st, "Pattern")
+  if (node.superClass) c(node.superClass, st, "Expression")
+  for (let i = 0; i < node.body.body.length; i++)
+    c(node.body.body[i], st)
+}
+base.MethodDefinition = base.Property = (node, st, c) => {
+  if (node.computed) c(node.key, st, "Expression")
+  c(node.value, st, "Expression")
+}
+base.ComprehensionExpression = (node, st, c) => {
+  for (let i = 0; i < node.blocks.length; i++)
+    c(node.blocks[i].right, st, "Expression")
+  c(node.body, st, "Expression")
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/whitespace.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/whitespace.js
new file mode 100644
index 0000000..1f4404a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/node_modules/acorn/src/whitespace.js
@@ -0,0 +1,12 @@
+// Matches a whole line break (where CRLF is considered a single
+// line break). Used to count lines.
+
+export const lineBreak = /\r\n?|\n|\u2028|\u2029/
+export const lineBreakG = new RegExp(lineBreak.source, "g")
+
+export function isNewLine(code) {
+  return code === 10 || code === 13 || code === 0x2028 || code == 0x2029
+}
+
+export const nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/package.json
new file mode 100644
index 0000000..86ed9d4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/package.json
@@ -0,0 +1,50 @@
+{
+  "name": "constantinople",
+  "version": "3.0.2",
+  "description": "Determine whether a JavaScript expression evaluates to a constant (using UglifyJS)",
+  "keywords": [],
+  "dependencies": {
+    "acorn": "^2.1.0"
+  },
+  "devDependencies": {
+    "mocha": "*"
+  },
+  "scripts": {
+    "test": "mocha -R spec"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/ForbesLindesay/constantinople.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "8947fdfb41428b1c8f5df1645f38bc0af34d7f21",
+  "bugs": {
+    "url": "https://github.com/ForbesLindesay/constantinople/issues"
+  },
+  "homepage": "https://github.com/ForbesLindesay/constantinople",
+  "_id": "constantinople@3.0.2",
+  "_shasum": "4b945d9937907bcd98ee575122c3817516544141",
+  "_from": "constantinople@>=3.0.1 <4.0.0",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "4b945d9937907bcd98ee575122c3817516544141",
+    "tarball": "http://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/test/index.js
new file mode 100644
index 0000000..6fd614b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/constantinople/test/index.js
@@ -0,0 +1,71 @@
+'use strict'
+
+var assert = require('assert')
+var constaninople = require('../')
+
+describe('isConstant(src)', function () {
+  it('handles "[5 + 3 + 10]"', function () {
+    assert(constaninople.isConstant('[5 + 3 + 10]') === true)
+  })
+  it('handles "/[a-z]/.test(\'a\')"', function () {
+    assert(constaninople.isConstant('/[a-z]/.test(\'a\')') === true)
+  })
+  it('handles "{\'class\': [(\'data\')]}"', function () {
+    assert(constaninople.isConstant('{\'class\': [(\'data\')]}') === true)
+  })
+  it('handles "Math.random()"', function () {
+    assert(constaninople.isConstant('Math.random()') === false)
+  })
+  it('handles "Math.random("', function () {
+    assert(constaninople.isConstant('Math.random(') === false)
+  })
+  it('handles "Math.floor(10.5)" with {Math: Math} as constants', function () {
+    assert(constaninople.isConstant('Math.floor(10.5)', {Math: Math}) === true)
+  })
+  it('handles "this.myVar"', function () {
+    assert(constaninople.isConstant('this.myVar') === false)
+  })
+  it('handles "(function () { while (true); return 10; }())"', function () {
+    assert(constaninople.isConstant('(function () { while (true); return 10; }())') === false)
+  })
+})
+
+
+describe('toConstant(src)', function () {
+  it('handles "[5 + 3 + 10]"', function () {
+    assert.deepEqual(constaninople.toConstant('[5 + 3 + 10]'), [5 + 3 + 10])
+  })
+  it('handles "/[a-z]/.test(\'a\')"', function () {
+    assert(constaninople.toConstant('/[a-z]/.test(\'a\')') === true)
+  })
+  it('handles "{\'class\': [(\'data\')]}"', function () {
+    assert.deepEqual(constaninople.toConstant('{\'class\': [(\'data\')]}'), {'class': ['data']})
+  })
+  it('handles "Math.random()"', function () {
+    try {
+      constaninople.toConstant('Math.random()')
+    } catch (ex) {
+      return
+    }
+    assert(false, 'Math.random() should result in an error')
+  })
+  it('handles "Math.random("', function () {
+    try {
+      constaninople.toConstant('Math.random(')
+    } catch (ex) {
+      return
+    }
+    assert(false, 'Math.random( should result in an error')
+  })
+  it('handles "Math.floor(10.5)" with {Math: Math} as constants', function () {
+    assert(constaninople.toConstant('Math.floor(10.5)', {Math: Math}) === 10)
+  })
+  it('handles "(function () { while (true); return 10; }())"', function () {
+    try {
+      constaninople.toConstant('(function () { while (true); return 10; }())')
+    } catch (ex) {
+      return
+    }
+    assert(false, '(function () { while (true); return 10; }()) should result in an error')
+  })
+})
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/README.md
new file mode 100644
index 0000000..61ecc5a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/README.md
@@ -0,0 +1,22 @@
+# jade-error
+
+Standard error objects for jade.  This module is intended for use by the lexer, parser, loader, linker, code-generator and any plugins.
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-error/master.svg)](https://travis-ci.org/jadejs/jade-error)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-error.svg)](https://gemnasium.com/jadejs/jade-error)
+[![NPM version](https://img.shields.io/npm/v/jade-error.svg)](https://www.npmjs.org/package/jade-error)
+
+## Installation
+
+    npm install jade-error
+
+## Usage
+
+```js
+var err = error('MY_CODE', 'My message', {line: 3, filename: 'myfile', src: 'foo\nbar\nbaz\nbash\nbing'});
+throw err;
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/index.js
new file mode 100644
index 0000000..6d85037
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/index.js
@@ -0,0 +1,32 @@
+'use strict';
+
+module.exports = makeError;
+function makeError(code, message, options) {
+  var line = options.line;
+  var filename = options.filename;
+  var src = options.src;
+  var fullMessage;
+  if (src) {
+    var lines = src.split('\n');
+    var start = Math.max(line - 3, 0)
+    var end = Math.min(lines.length, line + 3);
+    // Error context
+    var context = lines.slice(start, end).map(function(text, i){
+      var curr = i + start + 1;
+      return (curr == line ? '  > ' : '    ')
+        + curr
+        + '| '
+        + text;
+    }).join('\n');
+    fullMessage = (filename || 'Jade') + ':' + line + '\n' + context + '\n\n' + message;
+  } else {
+    fullMessage = (filename || 'Jade') + ':' + line + '\n\n' + message;
+  }
+  var err = new Error(fullMessage);
+  err.code = 'JADE:' + code;
+  err.msg = message;
+  err.line = line;
+  err.filename = filename;
+  err.src = src;
+  return err;
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/package.json
new file mode 100644
index 0000000..d8e7a47
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-error/package.json
@@ -0,0 +1,52 @@
+{
+  "name": "jade-error",
+  "version": "1.0.0",
+  "description": "Standard error objects for jade",
+  "keywords": [],
+  "files": [
+    "index.js"
+  ],
+  "dependencies": {},
+  "devDependencies": {
+    "istanbul": "^0.3.17",
+    "testit": "^2.0.2"
+  },
+  "scripts": {
+    "test": "node test",
+    "coverage": "istanbul cover test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-error.git"
+  },
+  "author": {
+    "name": "Forbes Lindesay"
+  },
+  "license": "MIT",
+  "gitHead": "20c1ec8ebbfcdd694604551c5fa31e9f35de196e",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-error/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-error",
+  "_id": "jade-error@1.0.0",
+  "_shasum": "ecb0a1df423f39098ebebee4defbc751aa06d320",
+  "_from": "jade-error@>=1.0.0 <2.0.0",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "ecb0a1df423f39098ebebee4defbc751aa06d320",
+    "tarball": "http://registry.npmjs.org/jade-error/-/jade-error-1.0.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-error/-/jade-error-1.0.0.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/.npmignore
new file mode 100644
index 0000000..b83202d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/.npmignore
@@ -0,0 +1,13 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/README.md
new file mode 100644
index 0000000..a590f1f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/README.md
@@ -0,0 +1,37 @@
+# jade-walk
+
+Walk and transform a jade AST
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-walk/master.svg)](https://travis-ci.org/jadejs/jade-walk)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-walk.svg)](https://gemnasium.com/jadejs/jade-walk)
+[![NPM version](https://img.shields.io/npm/v/jade-walk.svg)](https://www.npmjs.org/package/jade-walk)
+
+## Installation
+
+    npm install jade-walk
+
+## Usage
+
+
+```js
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var walk = require('jade-walk');
+
+var ast = walk(parse(lex('.my-class foo')), function before(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+  // return `false` to skip descending
+  if (node.type === 'Text') {
+    replace({ type: 'Text', val: 'bar', line: node.line });
+  }
+}, function after(node, replace) {
+  // called after walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+}, {includeDependencies: true});
+assert.deepEqual(parse(lex('.my-class bar')), ast);
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/index.js
new file mode 100644
index 0000000..29289a9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/index.js
@@ -0,0 +1,58 @@
+'use strict';
+
+module.exports = walkAST;
+function walkAST(ast, before, after, options) {
+  if (after && typeof after === 'object' && typeof options === 'undefined') {
+    options = after;
+    after = null;
+  }
+  options = options || {includeDependencies: false};
+  function replace(replacement) {
+    ast = replacement;
+  }
+  var result = before && before(ast, replace);
+  if (before && result === false) {
+    return ast;
+  }
+  switch (ast.type) {
+    case 'NamedBlock':
+    case 'Block':
+      ast.nodes = ast.nodes.map(function (node) {
+        return walkAST(node, before, after, options);
+      });
+      break;
+    case 'Case':
+    case 'Each':
+    case 'Mixin':
+    case 'Tag':
+    case 'When':
+    case 'Code':
+      if (ast.block) {
+        ast.block = walkAST(ast.block, before, after, options);
+      }
+      break;
+    case 'Extends':
+    case 'Include':
+      walkAST(ast.file, before, after, options);
+      break;
+    case 'Attrs':
+    case 'BlockComment':
+    case 'Comment':
+    case 'Doctype':
+    case 'Filter':
+    case 'Literal':
+    case 'MixinBlock':
+    case 'Text':
+      break;
+    case 'FileReference':
+      if (options.includeDependencies && ast.ast) {
+        walkAST(ast.ast, before, after, options);
+      }
+      break;
+    default:
+      throw new Error('Unexpected node type ' + ast.type);
+      break;
+  }
+  after && after(ast, replace);
+  return ast;
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/package.json
new file mode 100644
index 0000000..e6aa95a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/package.json
@@ -0,0 +1,50 @@
+{
+  "name": "jade-walk",
+  "version": "0.0.2",
+  "description": "Walk and transform a jade AST",
+  "keywords": [
+    "jade"
+  ],
+  "dependencies": {},
+  "devDependencies": {
+    "jade-lexer": "0.0.5",
+    "jade-parser": "0.0.7"
+  },
+  "scripts": {
+    "test": "node test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-walk.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "7589c5862d50b01b00256a3c034a7d6bbf1b8a14",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-walk/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-walk",
+  "_id": "jade-walk@0.0.2",
+  "_shasum": "d32518f3a273ddbca32827272b46a1d96a1c1f64",
+  "_from": "jade-walk@0.0.2",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "d32518f3a273ddbca32827272b46a1d96a1c1f64",
+    "tarball": "http://registry.npmjs.org/jade-walk/-/jade-walk-0.0.2.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-walk/-/jade-walk-0.0.2.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/test.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/test.js
new file mode 100644
index 0000000..899534b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jade-walk/test.js
@@ -0,0 +1,20 @@
+'use strict';
+
+var assert = require('assert');
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var walk = require('./');
+
+var ast = walk(parse(lex('.my-class foo')), function before(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+  // return `false` to skip descending
+  if (node.type === 'Text') {
+    replace({ type: 'Text', val: 'bar', line: node.line });
+  }
+}, function after(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+});
+assert.deepEqual(parse(lex('.my-class bar')), ast);
+console.log('tests passed');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/LICENSE
new file mode 100644
index 0000000..27cc9f3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/README.md
new file mode 100644
index 0000000..f294e79
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/README.md
@@ -0,0 +1,103 @@
+<p align="center"><img src="https://cdn.rawgit.com/jstransformers/jstransformer/2bb6dc6c410e8683a17a4af5f1b73bcbee95aada/logo.svg" width="300px" height="299px" /></p>
+<h1 align="center">JSTransformer</h1>
+<p align="center">Normalize the API of any jstransformer</p>
+
+<p align="center"><a href="https://travis-ci.org/jstransformers/jstransformer"><img src="https://img.shields.io/travis/jstransformers/jstransformer/master.svg" alt="Build Status"></a>
+<a href="https://gemnasium.com/jstransformers/jstransformer"><img src="https://img.shields.io/gemnasium/jstransformers/jstransformer.svg" alt="Dependency Status"></a>
+<a href="https://coveralls.io/r/jstransformers/jstransformer?branch=master"><img src="https://img.shields.io/coveralls/jstransformers/jstransformer/master.svg" alt="Coverage Status"></a>
+<a href="https://www.npmjs.org/package/jstransformer"><img src="https://img.shields.io/npm/v/jstransformer.svg" alt="NPM version"></a></p>
+
+## Installation
+
+    npm install jstransformer
+
+## Usage
+
+```js
+var transformer = require('jstransformer');
+var marked = transformer(require('jstransformer-marked'));
+
+var options = {};
+var res = marked.render('Some **markdown**', options);
+// => {body: 'Some <strong>markdown</strong>', dependencies: []}
+```
+
+This gives the same API regardless of the jstransformer passed in.
+
+## API
+
+A transformer, once normalised using this module, will implement the following methods.  Note that if the underlying transformer cannot be used to implement the functionality, it may ultimately just throw an error.
+
+### Returned object from `.render*`
+
+```js
+{body: String, dependencies: Array.<String>}
+```
+
+ - `body` represents the result as a string
+ - `dependencies` is an array of files that were read in as part of the render process (or an empty array if there were no dependencies)
+
+### `.render`
+
+```js
+transformer.render(str, options, locals);
+=> {body: String, dependencies: Array.<String>}
+```
+
+_requires the underlying transform to implement `.render` or `.compile`_
+
+Transform a string and return an object.
+
+### `.renderAsync`
+
+```js
+transformer.renderAsync(str[, options], locals, callback);
+```
+
+```js
+transformer.renderAsync(str[, options], locals);
+=> Promise({body: String, dependencies: Array.<String>})
+```
+
+_requires the underlying transform to implement `.renderAsync` or `.render`_
+
+Transform a string asynchronously. If a callback is provided, it is called as `callback(err, data)`, otherwise a Promise is returned.
+
+### `.renderFile`
+
+```js
+transformer.renderFile(filename, options, locals)
+=> {body: String, dependencies: Array.<String>}
+```
+
+_requires the underlying transform to implement `.renderFile`, `.render`, `.compileFile`, or `.compile`_
+
+Transform a file and return an object.
+
+### `.renderFileAsync`
+
+```js
+transformer.renderFileAsync(filename[, options], locals, callback);
+```
+
+```js
+transformer.renderFileAsync(filename[, options], locals);
+=> Promise({body: String, dependencies: Array.<String>})
+```
+
+_requires the underlying transform to implement `.renderFileAsync`, `.renderFile`, `.renderAsync`, `.render`, `.compileFileAsync`, `.compileFile`, `.compileAsync`, or `.compileFile`_
+
+Transform a file asynchronously. If a callback is provided, it is called as `callback(err, data)`, otherwise a Promise is returned.
+
+### `.inputFormats`
+
+```js
+var formats = transformer.inputFormats;
+=> ['md', 'markdown']
+```
+
+Returns an array of strings representing potential input formats for the transform. If not provided directly by the transform, results in an array containing the name of the transform.
+
+## License
+
+MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/index.js
new file mode 100644
index 0000000..ba00b9c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/index.js
@@ -0,0 +1,328 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var Promise = require('promise');
+var isPromise = require('is-promise');
+
+var tr = (module.exports = function (transformer) {
+  return new Transformer(transformer);
+});
+tr.Transformer = Transformer;
+tr.normalizeFn = normalizeFn;
+tr.normalizeFnAsync = normalizeFnAsync;
+tr.normalize = normalize;
+tr.normalizeAsync = normalizeAsync;
+tr.readFile = Promise.denodeify(fs.readFile);
+tr.readFileSync = fs.readFileSync;
+
+function normalizeFn(result) {
+  if (typeof result === 'function') {
+    return {fn: result, dependencies: []};
+  } else if (result && typeof result === 'object' && typeof result.fn === 'function') {
+    if ('dependencies' in result) {
+      if (!Array.isArray(result.dependencies)) {
+        throw new Error('Result should have a dependencies property that is an array');
+      }
+    } else {
+      result.dependencies = [];
+    }
+    return result;
+  } else {
+    throw new Error('Invalid result object from transform.');
+  }
+}
+function normalizeFnAsync(result, cb) {
+  return Promise.resolve(result).then(function (result) {
+    if (result && isPromise(result.fn)) {
+      return result.fn.then(function (fn) {
+        result.fn = fn;
+        return result;
+      });
+    }
+    return result;
+  }).then(tr.normalizeFn).nodeify(cb);
+}
+function normalize(result) {
+  if (typeof result === 'string') {
+    return {body: result, dependencies: []};
+  } else if (result && typeof result === 'object' && typeof result.body === 'string') {
+    if ('dependencies' in result) {
+      if (!Array.isArray(result.dependencies)) {
+        throw new Error('Result should have a dependencies property that is an array');
+      }
+    } else {
+      result.dependencies = [];
+    }
+    return result;
+  } else {
+    throw new Error('Invalid result object from transform.');
+  }
+}
+function normalizeAsync(result, cb) {
+  return Promise.resolve(result).then(function (result) {
+    if (result && isPromise(result.body)) {
+      return result.body.then(function (body) {
+        result.body = body;
+        return result;
+      });
+    }
+    return result;
+  }).then(tr.normalize).nodeify(cb);
+}
+
+function Transformer(tr) {
+  assert(tr, 'Transformer must be an object');
+  assert(typeof tr.name === 'string', 'Transformer must have a name');
+  assert(typeof tr.outputFormat === 'string', 'Transformer must have an output format');
+  assert([
+    'compile',
+    'compileAsync',
+    'compileFile',
+    'compileFileAsync',
+    'compileClient',
+    'compileClientAsync',
+    'compileFileClient',
+    'compileFileClientAsync',
+    'render',
+    'renderAsync',
+    'renderFile',
+    'renderFileAsync'
+  ].some(function (method) {
+    return typeof tr[method] === 'function';
+  }), 'Transformer must implement at least one of the potential methods.');
+  this._tr = tr;
+  this.name = this._tr.name;
+  this.outputFormat = this._tr.outputFormat;
+  this.inputFormats = this._tr.inputFormats || [this.name];
+}
+
+var fallbacks = {
+  compile: ['compile'],
+  compileAsync: ['compileAsync', 'compile'],
+  compileFile: ['compileFile', 'compile'],
+  compileFileAsync: ['compileFileAsync', 'compileFile', 'compileAsync', 'compile'],
+  compileClient: ['compileClient'],
+  compileClientAsync: ['compileClientAsync', 'compileClient'],
+  compileFileClient: ['compileFileClient', 'compileClient'],
+  compileFileClientAsync: [
+    'compileFileClientAsync', 'compileFileClient', 'compileClientAsync', 'compileClient'
+  ],
+  render: ['render', 'compile'],
+  renderAsync: ['renderAsync', 'render', 'compileAsync', 'compile'],
+  renderFile: ['renderFile', 'render', 'compileFile', 'compile'],
+  renderFileAsync: [
+    'renderFileAsync', 'renderFile', 'renderAsync', 'render',
+    'compileFileAsync', 'compileFile', 'compileAsync', 'compile'
+  ]
+};
+
+Transformer.prototype._hasMethod = function (method) {
+  return typeof this._tr[method] === 'function';
+};
+Transformer.prototype.can = function (method) {
+  return fallbacks[method].some(function (method) {
+    return this._hasMethod(method);
+  }.bind(this));
+};
+
+/* COMPILE */
+
+Transformer.prototype.compile = function (str, options) {
+  if (!this.can('compile')) {
+    if (this.can('compileAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support synchronous compilation');
+    } else if (this.can('compileFileAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support compiling plain strings');
+    } else {
+      throw new Error('The Transform "' + this.name + '" does not support compilation');
+    }
+  }
+  return tr.normalizeFn(this._tr.compile(str, options));
+};
+Transformer.prototype.compileAsync = function (str, options, cb) {
+  if (!this.can('compileAsync')) {
+    if (this.can('compileFileAsync')) {
+      return Promise.reject(new Error('The Transform "' + this.name + '" does not support compiling plain strings')).nodeify(cb);
+    } else {
+      return Promise.reject(new Error('The Transform "' + this.name + '" does not support compilation')).nodeify(cb);
+    }
+  }
+  if (this._hasMethod('compileAsync')) {
+    return tr.normalizeFnAsync(this._tr.compileAsync(str, options), cb);
+  } else {
+    return tr.normalizeFnAsync(this._tr.compile(str, options), cb);
+  }
+};
+Transformer.prototype.compileFile = function (filename, options) {
+  if (!this.can('compileFile')) {
+    if (this.can('compileFileAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support synchronous compilation');
+    } else {
+      throw new Error('The Transform "' + this.name + '" does not support compilation');
+    }
+  }
+  if (this._hasMethod('compileFile')) {
+    return tr.normalizeFn(this._tr.compileFile(filename, options));
+  } else {
+    return tr.normalizeFn(this._tr.compile(tr.readFileSync(filename, 'utf8'), options));
+  }
+};
+Transformer.prototype.compileFileAsync = function (filename, options, cb) {
+  if (!this.can('compileFileAsync')) {
+    return Promise.reject(new Error('The Transform "' + this.name + '" does not support compilation'));
+  }
+  if (this._hasMethod('compileFileAsync')) {
+    return tr.normalizeFnAsync(this._tr.compileFileAsync(filename, options), cb);
+  } else if (this._hasMethod('compileFile')) {
+    return tr.normalizeFnAsync(this._tr.compileFile(filename, options), cb);
+  } else {
+    return tr.normalizeFnAsync(tr.readFile(filename, 'utf8').then(function (str) {
+      if (this._hasMethod('compileAsync')) {
+        return this._tr.compileAsync(str, options);
+      } else {
+        return this._tr.compile(str, options);
+      }
+    }.bind(this)), cb);
+  }
+};
+
+/* COMPILE CLIENT */
+
+
+Transformer.prototype.compileClient = function (str, options) {
+  if (!this.can('compileClient')) {
+    if (this.can('compileClientAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support compiling for the client synchronously.');
+    } else if (this.can('compileFileClientAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support compiling for the client from a string.');
+    } else {
+      throw new Error('The Transform "' + this.name + '" does not support compiling for the client');
+    }
+  }
+  return tr.normalize(this._tr.compileClient(str, options));
+};
+Transformer.prototype.compileClientAsync = function (str, options, cb) {
+  if (!this.can('compileClientAsync')) {
+    if (this.can('compileFileClientAsync')) {
+      return Promise.reject(new Error('The Transform "' + this.name + '" does not support compiling for the client from a string.')).nodeify(cb);
+    } else {
+      return Promise.reject(new Error('The Transform "' + this.name + '" does not support compiling for the client')).nodeify(cb);
+    }
+  }
+  if (this._hasMethod('compileClientAsync')) {
+    return tr.normalizeAsync(this._tr.compileClientAsync(str, options), cb);
+  } else {
+    return tr.normalizeAsync(this._tr.compileClient(str, options), cb);
+  }
+};
+Transformer.prototype.compileFileClient = function (filename, options) {
+  if (!this.can('compileFileClient')) {
+    if (this.can('compileFileClientAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support compiling for the client synchronously.');
+    } else {
+      throw new Error('The Transform "' + this.name + '" does not support compiling for the client');
+    }
+  }
+  if (this._hasMethod('compileFileClient')) {
+    return tr.normalize(this._tr.compileFileClient(filename, options));
+  } else {
+    return tr.normalize(this._tr.compileClient(tr.readFileSync(filename, 'utf8'), options));
+  }
+};
+Transformer.prototype.compileFileClientAsync = function (filename, options, cb) {
+  if (!this.can('compileFileClientAsync')) {
+    return Promise.reject(new Error('The Transform "' + this.name + '" does not support compiling for the client')).nodeify(cb)
+  }
+  if (this._hasMethod('compileFileClientAsync')) {
+    return tr.normalizeAsync(this._tr.compileFileClientAsync(filename, options), cb);
+  } else if (this._hasMethod('compileFileClient')) {
+    return tr.normalizeAsync(this._tr.compileFileClient(filename, options), cb);
+  } else {
+    return tr.normalizeAsync(tr.readFile(filename, 'utf8').then(function (str) {
+      if (this._hasMethod('compileClientAsync')) {
+        return this._tr.compileClientAsync(str, options);
+      } else {
+        return this._tr.compileClient(str, options);
+      }
+    }.bind(this)), cb);
+  }
+};
+
+/* RENDER */
+
+Transformer.prototype.render = function (str, options, locals) {
+  if (!this.can('render')) {
+    if (this.can('renderAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support rendering synchronously.');
+    } else if (this.can('renderFileAsync')) {
+      throw new Error('The Transform "' + this.name + '" does not support rendering from a string.');
+    } else {
+      throw new Error('The Transform "' + this.name + '" does not support rendering');
+    }
+  }
+  if (this._hasMethod('render')) {
+    return tr.normalize(this._tr.render(str, options, locals));
+  } else {
+    var compiled = tr.normalizeFn(this._tr.compile(str, options));
+    var body = compiled.fn(options || locals);
+    if (typeof body !== 'string') {
+      throw new Error('The Transform "' + this.name + '" does not support rendering synchronously.');
+    }
+    return tr.normalize({body: body, dependencies: compiled.dependencies});
+  }
+};
+Transformer.prototype.renderAsync = function (str, options, locals, cb) {
+  if (typeof locals === 'function') {
+    cb = locals;
+    locals = options;
+  }
+  if (!this.can('renderAsync')) {
+    if (this.can('renderFileAsync')) {
+      return Promise.reject(new Error('The Transform "' + this.name + '" does not support rendering from a string.')).nodeify(cb);
+    } else {
+      return Promise.reject(new Error('The Transform "' + this.name + '" does not support rendering')).nodeify(cb);
+    }
+  }
+  if (this._hasMethod('renderAsync')) {
+    return tr.normalizeAsync(this._tr.renderAsync(str, options, locals), cb);
+  } else if (this._hasMethod('render')) {
+    return tr.normalizeAsync(this._tr.render(str, options, locals), cb);
+  } else {
+    return tr.normalizeAsync(this.compileAsync(str, options).then(function (compiled) {
+      return {body: compiled.fn(options || locals), dependencies: compiled.dependencies};
+    }), cb);
+  }
+};
+Transformer.prototype.renderFile = function (filename, options, locals) {
+  if (typeof this._tr.renderFile === 'function') {
+    return tr.normalize(this._tr.renderFile(filename, options, locals));
+  } else if (typeof this._tr.render === 'function') {
+    return tr.normalize(this._tr.render(tr.readFileSync(filename, 'utf8'), options, locals));
+  } else if (this._hasMethod('compile') || this._hasMethod('compileFile')) {
+    var compiled = this.compileFile(filename, options);
+    return tr.normalize({body: compiled.fn(options || locals), dependencies: compiled.dependencies});
+  } else {
+    return Promise.reject(new Error('This transform does not support synchronous rendering'));
+  }
+};
+Transformer.prototype.renderFileAsync = function (filename, options, locals, cb) {
+  if (typeof locals === 'function') {
+    cb = locals;
+    locals = options;
+  }
+  if (typeof this._tr.renderFileAsync === 'function') {
+    return tr.normalizeAsync(this._tr.renderFileAsync(filename, options, locals), cb);
+  } else if (typeof this._tr.renderFile === 'function') {
+    return tr.normalizeAsync(this._tr.renderFile(filename, options, locals), cb);
+  } else if (this._hasMethod('compile') || this._hasMethod('compileAsync')
+             || this._hasMethod('compileFile') || this._hasMethod('compileFileAsync')) {
+    return tr.normalizeAsync(this.compileFileAsync(filename, options).then(function (compiled) {
+      return {body: compiled.fn(options || locals), dependencies: compiled.dependencies};
+    }), cb);
+  } else {
+    return tr.normalizeAsync(tr.readFile(filename, 'utf8').then(function (str) {
+      return this.renderAsync(str, options, locals);
+    }.bind(this)), cb);
+  }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/.npmignore
new file mode 100644
index 0000000..aeb7b45
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/.npmignore
@@ -0,0 +1,6 @@
+component
+build
+node_modules
+test.js
+component.json
+.gitignore
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/.travis.yml
new file mode 100644
index 0000000..87f8cd9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/LICENSE
new file mode 100644
index 0000000..27cc9f3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/index.js
new file mode 100644
index 0000000..ca2444c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/index.js
@@ -0,0 +1,5 @@
+module.exports = isPromise;
+
+function isPromise(obj) {
+  return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/package.json
new file mode 100644
index 0000000..211a1da
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "is-promise",
+  "version": "2.1.0",
+  "description": "Test whether an object looks like a promises-a+ promise",
+  "main": "index.js",
+  "scripts": {
+    "test": "mocha -R spec"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/then/is-promise.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "devDependencies": {
+    "better-assert": "~0.1.0",
+    "mocha": "~1.7.4"
+  },
+  "gitHead": "056f8ac12eed91886ac4f0f7d872a176f6ed698f",
+  "bugs": {
+    "url": "https://github.com/then/is-promise/issues"
+  },
+  "homepage": "https://github.com/then/is-promise",
+  "_id": "is-promise@2.1.0",
+  "_shasum": "79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa",
+  "_from": "is-promise@>=2.0.0 <3.0.0",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa",
+    "tarball": "http://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/readme.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/readme.md
new file mode 100644
index 0000000..50d5d98
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/is-promise/readme.md
@@ -0,0 +1,29 @@
+<a href="http://promises-aplus.github.com/promises-spec"><img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png" align="right" /></a>
+# is-promise
+
+  Test whether an object looks like a promises-a+ promise
+
+ [![Build Status](https://img.shields.io/travis/then/is-promise/master.svg)](https://travis-ci.org/then/is-promise)
+ [![Dependency Status](https://img.shields.io/gemnasium/then/is-promise.svg)](https://gemnasium.com/then/is-promise)
+ [![NPM version](https://img.shields.io/npm/v/is-promise.svg)](https://www.npmjs.org/package/is-promise)
+
+## Installation
+
+    $ npm install is-promise
+
+You can also use it client side via npm.
+
+## API
+
+```javascript
+var isPromise = require('is-promise');
+
+isPromise({then:function () {...}});//=>true
+isPromise(null);//=>false
+isPromise({});//=>false
+isPromise({then: true})//=>false
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/.jshintrc b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/.jshintrc
new file mode 100644
index 0000000..47c256f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/.jshintrc
@@ -0,0 +1,5 @@
+{
+  "asi": true,
+  "node": true,
+  "strict": true
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/.npmignore
new file mode 100644
index 0000000..59bed65
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/.npmignore
@@ -0,0 +1,6 @@
+components
+node_modules
+test
+.gitignore
+.travis.yml
+component.json
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/LICENSE
new file mode 100644
index 0000000..7a1f763
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/Readme.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/Readme.md
new file mode 100644
index 0000000..cc5b263
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/Readme.md
@@ -0,0 +1,221 @@
+<a href="https://promisesaplus.com/"><img src="https://promisesaplus.com/assets/logo-small.png" align="right" /></a>
+# promise
+
+This is a simple implementation of Promises.  It is a super set of ES6 Promises designed to have readable, performant code and to provide just the extensions that are absolutely necessary for using promises today.
+
+For detailed tutorials on its use, see www.promisejs.org
+
+[![Build Status](https://img.shields.io/travis/then/promise/master.svg)](https://travis-ci.org/then/promise)
+[![Dependency Status](https://img.shields.io/gemnasium/then/promise.svg)](https://gemnasium.com/then/promise)
+[![NPM version](https://img.shields.io/npm/v/promise.svg)](https://www.npmjs.org/package/promise)
+
+## Installation
+
+**Server:**
+
+    $ npm install promise
+
+**Client:**
+
+You can use browserify on the client, or use the pre-compiled script that acts as a polyfill.
+
+```html
+<script src="https://www.promisejs.org/polyfills/promise-6.1.0.js"></script>
+```
+
+Note that the [es5-shim](https://github.com/es-shims/es5-shim) must be loaded before this library to support browsers pre IE9.
+
+```html
+<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/3.4.0/es5-shim.min.js"></script>
+```
+
+## Usage
+
+The example below shows how you can load the promise library (in a way that works on both client and server).  It then demonstrates creating a promise from scratch.  You simply call `new Promise(fn)`.  There is a complete specification for what is returned by this method in [Promises/A+](http://promises-aplus.github.com/promises-spec/).
+
+```javascript
+var Promise = require('promise');
+
+var promise = new Promise(function (resolve, reject) {
+  get('http://www.google.com', function (err, res) {
+    if (err) reject(err);
+    else resolve(res);
+  });
+});
+```
+
+## API
+
+Before all examples, you will need:
+
+```js
+var Promise = require('promise');
+```
+
+### new Promise(resolver)
+
+This creates and returns a new promise.  `resolver` must be a function.  The `resolver` function is passed two arguments:
+
+ 1. `resolve` should be called with a single argument.  If it is called with a non-promise value then the promise is fulfilled with that value.  If it is called with a promise (A) then the returned promise takes on the state of that new promise (A).
+ 2. `reject` should be called with a single argument.  The returned promise will be rejected with that argument.
+
+### Static Functions
+
+  These methods are invoked by calling `Promise.methodName`.
+
+#### Promise.resolve(value)
+
+(deprecated aliases: `Promise.from(value)`, `Promise.cast(value)`)
+
+Converts values and foreign promises into Promises/A+ promises.  If you pass it a value then it returns a Promise for that value.  If you pass it something that is close to a promise (such as a jQuery attempt at a promise) it returns a Promise that takes on the state of `value` (rejected or fulfilled).
+
+#### Promise.all(array)
+
+Returns a promise for an array.  If it is called with a single argument that `Array.isArray` then this returns a promise for a copy of that array with any promises replaced by their fulfilled values.  Otherwise it returns a promise for an array that conatins its arguments, except with promises replaced by their resolution values.  e.g.
+
+```js
+Promise.all([Promise.resolve('a'), 'b', Promise.resolve('c')])
+  .then(function (res) {
+    assert(res[0] === 'a')
+    assert(res[1] === 'b')
+    assert(res[2] === 'c')
+  })
+
+Promise.all(Promise.resolve('a'), 'b', Promise.resolve('c'))
+  .then(function (res) {
+    assert(res[0] === 'a')
+    assert(res[1] === 'b')
+    assert(res[2] === 'c')
+  })
+```
+
+#### Promise.denodeify(fn)
+
+_Non Standard_
+
+Takes a function which accepts a node style callback and returns a new function that returns a promise instead.
+
+e.g.
+
+```javascript
+var fs = require('fs')
+
+var read = Promise.denodeify(fs.readFile)
+var write = Promise.denodeify(fs.writeFile)
+
+var p = read('foo.json', 'utf8')
+  .then(function (str) {
+    return write('foo.json', JSON.stringify(JSON.parse(str), null, '  '), 'utf8')
+  })
+```
+
+#### Promise.nodeify(fn)
+
+_Non Standard_
+
+The twin to `denodeify` is useful when you want to export an API that can be used by people who haven't learnt about the brilliance of promises yet.
+
+```javascript
+module.exports = Promise.nodeify(awesomeAPI)
+function awesomeAPI(a, b) {
+  return download(a, b)
+}
+```
+
+If the last argument passed to `module.exports` is a function, then it will be treated like a node.js callback and not parsed on to the child function, otherwise the API will just return a promise.
+
+### Prototype Methods
+
+These methods are invoked on a promise instance by calling `myPromise.methodName`
+
+### Promise#then(onFulfilled, onRejected)
+
+This method follows the [Promises/A+ spec](http://promises-aplus.github.io/promises-spec/).  It explains things very clearly so I recommend you read it.
+
+Either `onFulfilled` or `onRejected` will be called and they will not be called more than once.  They will be passed a single argument and will always be called asynchronously (in the next turn of the event loop).
+
+If the promise is fulfilled then `onFulfilled` is called.  If the promise is rejected then `onRejected` is called.
+
+The call to `.then` also returns a promise.  If the handler that is called returns a promise, the promise returned by `.then` takes on the state of that returned promise.  If the handler that is called returns a value that is not a promise, the promise returned by `.then` will be fulfilled with that value. If the handler that is called throws an exception then the promise returned by `.then` is rejected with that exception.
+
+#### Promise#catch(onRejected)
+
+Sugar for `Promise#then(null, onRejected)`, to mirror `catch` in synchronous code.
+
+#### Promise#done(onFulfilled, onRejected)
+
+_Non Standard_
+
+The same semantics as `.then` except that it does not return a promise and any exceptions are re-thrown so that they can be logged (crashing the application in non-browser environments)
+
+#### Promise#nodeify(callback)
+
+_Non Standard_
+
+If `callback` is `null` or `undefined` it just returns `this`.  If `callback` is a function it is called with rejection reason as the first argument and result as the second argument (as per the node.js convention).
+
+This lets you write API functions that look like:
+
+```javascript
+function awesomeAPI(foo, bar, callback) {
+  return internalAPI(foo, bar)
+    .then(parseResult)
+    .then(null, retryErrors)
+    .nodeify(callback)
+}
+```
+
+People who use typical node.js style callbacks will be able to just pass a callback and get the expected behavior.  The enlightened people can not pass a callback and will get awesome promises.
+
+## Extending Promises
+
+  There are three options for extending the promises created by this library.
+
+### Inheritance
+
+  You can use inheritance if you want to create your own complete promise library with this as your basic starting point, perfect if you have lots of cool features you want to add.  Here is an example of a promise library called `Awesome`, which is built on top of `Promise` correctly.
+
+```javascript
+var Promise = require('promise');
+function Awesome(fn) {
+  if (!(this instanceof Awesome)) return new Awesome(fn);
+  Promise.call(this, fn);
+}
+Awesome.prototype = Object.create(Promise.prototype);
+Awesome.prototype.constructor = Awesome;
+
+//Awesome extension
+Awesome.prototype.spread = function (cb) {
+  return this.then(function (arr) {
+    return cb.apply(this, arr);
+  })
+};
+```
+
+  N.B. if you fail to set the prototype and constructor properly or fail to do Promise.call, things can fail in really subtle ways.
+
+### Wrap
+
+  This is the nuclear option, for when you want to start from scratch.  It ensures you won't be impacted by anyone who is extending the prototype (see below).
+
+```javascript
+function Uber(fn) {
+  if (!(this instanceof Uber)) return new Uber(fn);
+  var _prom = new Promise(fn);
+  this.then = _prom.then;
+}
+
+Uber.prototype.spread = function (cb) {
+  return this.then(function (arr) {
+    return cb.apply(this, arr);
+  })
+};
+```
+
+### Extending the Prototype
+
+  In general, you should never extend the prototype of this promise implimenation because your extensions could easily conflict with someone elses extensions.  However, this organisation will host a library of extensions which do not conflict with each other, so you can safely enable any of those.  If you think of an extension that we don't provide and you want to write it, submit an issue on this repository and (if I agree) I'll set you up with a repository and give you permission to commit to it.
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/core.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/core.js
new file mode 100644
index 0000000..5f332a2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/core.js
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = require('./lib/core.js');
+
+console.error('require("promise/core") is deprecated, use require("promise/lib/core") instead.');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/index.js
new file mode 100644
index 0000000..17d5ed1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/index.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = require('./lib/core.js')
+require('./lib/done.js')
+require('./lib/es6-extensions.js')
+require('./lib/node-extensions.js')
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/core.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/core.js
new file mode 100644
index 0000000..05a4313
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/core.js
@@ -0,0 +1,105 @@
+'use strict';
+
+var asap = require('asap')
+
+module.exports = Promise;
+function Promise(fn) {
+  if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
+  if (typeof fn !== 'function') throw new TypeError('not a function')
+  var state = null
+  var value = null
+  var deferreds = []
+  var self = this
+
+  this.then = function(onFulfilled, onRejected) {
+    return new self.constructor(function(resolve, reject) {
+      handle(new Handler(onFulfilled, onRejected, resolve, reject))
+    })
+  }
+
+  function handle(deferred) {
+    if (state === null) {
+      deferreds.push(deferred)
+      return
+    }
+    asap(function() {
+      var cb = state ? deferred.onFulfilled : deferred.onRejected
+      if (cb === null) {
+        (state ? deferred.resolve : deferred.reject)(value)
+        return
+      }
+      var ret
+      try {
+        ret = cb(value)
+      }
+      catch (e) {
+        deferred.reject(e)
+        return
+      }
+      deferred.resolve(ret)
+    })
+  }
+
+  function resolve(newValue) {
+    try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+      if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
+      if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
+        var then = newValue.then
+        if (typeof then === 'function') {
+          doResolve(then.bind(newValue), resolve, reject)
+          return
+        }
+      }
+      state = true
+      value = newValue
+      finale()
+    } catch (e) { reject(e) }
+  }
+
+  function reject(newValue) {
+    state = false
+    value = newValue
+    finale()
+  }
+
+  function finale() {
+    for (var i = 0, len = deferreds.length; i < len; i++)
+      handle(deferreds[i])
+    deferreds = null
+  }
+
+  doResolve(fn, resolve, reject)
+}
+
+
+function Handler(onFulfilled, onRejected, resolve, reject){
+  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
+  this.onRejected = typeof onRejected === 'function' ? onRejected : null
+  this.resolve = resolve
+  this.reject = reject
+}
+
+/**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+function doResolve(fn, onFulfilled, onRejected) {
+  var done = false;
+  try {
+    fn(function (value) {
+      if (done) return
+      done = true
+      onFulfilled(value)
+    }, function (reason) {
+      if (done) return
+      done = true
+      onRejected(reason)
+    })
+  } catch (ex) {
+    if (done) return
+    done = true
+    onRejected(ex)
+  }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/done.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/done.js
new file mode 100644
index 0000000..2da6208
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/done.js
@@ -0,0 +1,14 @@
+'use strict';
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+Promise.prototype.done = function (onFulfilled, onRejected) {
+  var self = arguments.length ? this.then.apply(this, arguments) : this
+  self.then(null, function (err) {
+    asap(function () {
+      throw err
+    })
+  })
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/es6-extensions.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/es6-extensions.js
new file mode 100644
index 0000000..084c1cd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/es6-extensions.js
@@ -0,0 +1,108 @@
+'use strict';
+
+//This file contains the ES6 extensions to the core Promises/A+ API
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+
+/* Static Functions */
+
+function ValuePromise(value) {
+  this.then = function (onFulfilled) {
+    if (typeof onFulfilled !== 'function') return this
+    return new Promise(function (resolve, reject) {
+      asap(function () {
+        try {
+          resolve(onFulfilled(value))
+        } catch (ex) {
+          reject(ex);
+        }
+      })
+    })
+  }
+}
+ValuePromise.prototype = Promise.prototype
+
+var TRUE = new ValuePromise(true)
+var FALSE = new ValuePromise(false)
+var NULL = new ValuePromise(null)
+var UNDEFINED = new ValuePromise(undefined)
+var ZERO = new ValuePromise(0)
+var EMPTYSTRING = new ValuePromise('')
+
+Promise.resolve = function (value) {
+  if (value instanceof Promise) return value
+
+  if (value === null) return NULL
+  if (value === undefined) return UNDEFINED
+  if (value === true) return TRUE
+  if (value === false) return FALSE
+  if (value === 0) return ZERO
+  if (value === '') return EMPTYSTRING
+
+  if (typeof value === 'object' || typeof value === 'function') {
+    try {
+      var then = value.then
+      if (typeof then === 'function') {
+        return new Promise(then.bind(value))
+      }
+    } catch (ex) {
+      return new Promise(function (resolve, reject) {
+        reject(ex)
+      })
+    }
+  }
+
+  return new ValuePromise(value)
+}
+
+Promise.all = function (arr) {
+  var args = Array.prototype.slice.call(arr)
+
+  return new Promise(function (resolve, reject) {
+    if (args.length === 0) return resolve([])
+    var remaining = args.length
+    function res(i, val) {
+      try {
+        if (val && (typeof val === 'object' || typeof val === 'function')) {
+          var then = val.then
+          if (typeof then === 'function') {
+            then.call(val, function (val) { res(i, val) }, reject)
+            return
+          }
+        }
+        args[i] = val
+        if (--remaining === 0) {
+          resolve(args);
+        }
+      } catch (ex) {
+        reject(ex)
+      }
+    }
+    for (var i = 0; i < args.length; i++) {
+      res(i, args[i])
+    }
+  })
+}
+
+Promise.reject = function (value) {
+  return new Promise(function (resolve, reject) { 
+    reject(value);
+  });
+}
+
+Promise.race = function (values) {
+  return new Promise(function (resolve, reject) { 
+    values.forEach(function(value){
+      Promise.resolve(value).then(resolve, reject);
+    })
+  });
+}
+
+/* Prototype Methods */
+
+Promise.prototype['catch'] = function (onRejected) {
+  return this.then(null, onRejected);
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/node-extensions.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/node-extensions.js
new file mode 100644
index 0000000..5e60f9e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/lib/node-extensions.js
@@ -0,0 +1,63 @@
+'use strict';
+
+//This file contains then/promise specific extensions that are only useful for node.js interop
+
+var Promise = require('./core.js')
+var asap = require('asap')
+
+module.exports = Promise
+
+/* Static Functions */
+
+Promise.denodeify = function (fn, argumentCount) {
+  argumentCount = argumentCount || Infinity
+  return function () {
+    var self = this
+    var args = Array.prototype.slice.call(arguments)
+    return new Promise(function (resolve, reject) {
+      while (args.length && args.length > argumentCount) {
+        args.pop()
+      }
+      args.push(function (err, res) {
+        if (err) reject(err)
+        else resolve(res)
+      })
+      var res = fn.apply(self, args)
+      if (res && (typeof res === 'object' || typeof res === 'function') && typeof res.then === 'function') {
+        resolve(res)
+      }
+    })
+  }
+}
+Promise.nodeify = function (fn) {
+  return function () {
+    var args = Array.prototype.slice.call(arguments)
+    var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null
+    var ctx = this
+    try {
+      return fn.apply(this, arguments).nodeify(callback, ctx)
+    } catch (ex) {
+      if (callback === null || typeof callback == 'undefined') {
+        return new Promise(function (resolve, reject) { reject(ex) })
+      } else {
+        asap(function () {
+          callback.call(ctx, ex)
+        })
+      }
+    }
+  }
+}
+
+Promise.prototype.nodeify = function (callback, ctx) {
+  if (typeof callback != 'function') return this
+
+  this.then(function (value) {
+    asap(function () {
+      callback.call(ctx, null, value)
+    })
+  }, function (err) {
+    asap(function () {
+      callback.call(ctx, err)
+    })
+  })
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/LICENSE.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/LICENSE.md
new file mode 100644
index 0000000..5d98ad8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/LICENSE.md
@@ -0,0 +1,20 @@
+
+Copyright 2009–2013 Contributors. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/README.md
new file mode 100644
index 0000000..9a42759
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/README.md
@@ -0,0 +1,81 @@
+
+# ASAP
+
+This `asap` CommonJS package contains a single `asap` module that
+exports a single `asap` function that executes a function **as soon as
+possible**.
+
+```javascript
+asap(function () {
+    // ...
+});
+```
+
+More formally, ASAP provides a fast event queue that will execute tasks
+until it is empty before yielding to the JavaScript engine's underlying
+event-loop.  When the event queue becomes non-empty, ASAP schedules a
+flush event, preferring for that event to occur before the JavaScript
+engine has an opportunity to perform IO tasks or rendering, thus making
+the first task and subsequent tasks semantically indistinguishable.
+ASAP uses a variety of techniques to preserve this invariant on
+different versions of browsers and NodeJS.
+
+By design, ASAP can starve the event loop on the theory that, if there
+is enough work to be done synchronously, albeit in separate events, long
+enough to starve input or output, it is a strong indicator that the
+program needs to push back on scheduling more work.
+
+Take care.  ASAP can sustain infinite recursive calls indefinitely
+without warning.  This is behaviorally equivalent to an infinite loop.
+It will not halt from a stack overflow, but it *will* chew through
+memory (which is an oddity I cannot explain at this time).  Just as with
+infinite loops, you can monitor a Node process for this behavior with a
+heart-beat signal.  As with infinite loops, a very small amount of
+caution goes a long way to avoiding problems.
+
+```javascript
+function loop() {
+    asap(loop);
+}
+loop();
+```
+
+ASAP is distinct from `setImmediate` in that it does not suffer the
+overhead of returning a handle and being possible to cancel.  For a
+`setImmediate` shim, consider [setImmediate][].
+
+[setImmediate]: https://github.com/noblejs/setimmediate
+
+If a task throws an exception, it will not interrupt the flushing of
+high-priority tasks.  The exception will be postponed to a later,
+low-priority event to avoid slow-downs, when the underlying JavaScript
+engine will treat it as it does any unhandled exception.
+
+## Heritage
+
+ASAP has been factored out of the [Q][] asynchronous promise library.
+It originally had a naïve implementation in terms of `setTimeout`, but
+[Malte Ubl][NonBlocking] provided an insight that `postMessage` might be
+useful for creating a high-priority, no-delay event dispatch hack.
+Since then, Internet Explorer proposed and implemented `setImmediate`.
+Robert Kratić began contributing to Q by measuring the performance of
+the internal implementation of `asap`, paying particular attention to
+error recovery.  Domenic, Robert, and I collectively settled on the
+current strategy of unrolling the high-priority event queue internally
+regardless of what strategy we used to dispatch the potentially
+lower-priority flush event.  Domenic went on to make ASAP cooperate with
+NodeJS domains.
+
+[Q]: https://github.com/kriskowal/q
+[NonBlocking]: http://www.nonblocking.io/2011/06/windownexttick.html
+
+For further reading, Nicholas Zakas provided a thorough article on [The
+Case for setImmediate][NCZ].
+
+[NCZ]: http://www.nczonline.net/blog/2013/07/09/the-case-for-setimmediate/
+
+## License
+
+Copyright 2009-2013 by Contributors
+MIT License (enclosed)
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/asap.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/asap.js
new file mode 100644
index 0000000..2f85516
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/asap.js
@@ -0,0 +1,113 @@
+
+// Use the fastest possible means to execute a task in a future turn
+// of the event loop.
+
+// linked list of tasks (single, with head node)
+var head = {task: void 0, next: null};
+var tail = head;
+var flushing = false;
+var requestFlush = void 0;
+var isNodeJS = false;
+
+function flush() {
+    /* jshint loopfunc: true */
+
+    while (head.next) {
+        head = head.next;
+        var task = head.task;
+        head.task = void 0;
+        var domain = head.domain;
+
+        if (domain) {
+            head.domain = void 0;
+            domain.enter();
+        }
+
+        try {
+            task();
+
+        } catch (e) {
+            if (isNodeJS) {
+                // In node, uncaught exceptions are considered fatal errors.
+                // Re-throw them synchronously to interrupt flushing!
+
+                // Ensure continuation if the uncaught exception is suppressed
+                // listening "uncaughtException" events (as domains does).
+                // Continue in next event to avoid tick recursion.
+                if (domain) {
+                    domain.exit();
+                }
+                setTimeout(flush, 0);
+                if (domain) {
+                    domain.enter();
+                }
+
+                throw e;
+
+            } else {
+                // In browsers, uncaught exceptions are not fatal.
+                // Re-throw them asynchronously to avoid slow-downs.
+                setTimeout(function() {
+                   throw e;
+                }, 0);
+            }
+        }
+
+        if (domain) {
+            domain.exit();
+        }
+    }
+
+    flushing = false;
+}
+
+if (typeof process !== "undefined" && process.nextTick) {
+    // Node.js before 0.9. Note that some fake-Node environments, like the
+    // Mocha test runner, introduce a `process` global without a `nextTick`.
+    isNodeJS = true;
+
+    requestFlush = function () {
+        process.nextTick(flush);
+    };
+
+} else if (typeof setImmediate === "function") {
+    // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
+    if (typeof window !== "undefined") {
+        requestFlush = setImmediate.bind(window, flush);
+    } else {
+        requestFlush = function () {
+            setImmediate(flush);
+        };
+    }
+
+} else if (typeof MessageChannel !== "undefined") {
+    // modern browsers
+    // http://www.nonblocking.io/2011/06/windownexttick.html
+    var channel = new MessageChannel();
+    channel.port1.onmessage = flush;
+    requestFlush = function () {
+        channel.port2.postMessage(0);
+    };
+
+} else {
+    // old browsers
+    requestFlush = function () {
+        setTimeout(flush, 0);
+    };
+}
+
+function asap(task) {
+    tail = tail.next = {
+        task: task,
+        domain: isNodeJS && process.domain,
+        next: null
+    };
+
+    if (!flushing) {
+        flushing = true;
+        requestFlush();
+    }
+};
+
+module.exports = asap;
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/package.json
new file mode 100644
index 0000000..71908b0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/node_modules/asap/package.json
@@ -0,0 +1,38 @@
+{
+  "name": "asap",
+  "version": "1.0.0",
+  "description": "High-priority task queue for Node.js and browsers",
+  "keywords": [
+    "event",
+    "task",
+    "queue"
+  ],
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/kriskowal/asap/raw/master/LICENSE.md"
+    }
+  ],
+  "main": "asap",
+  "_id": "asap@1.0.0",
+  "dist": {
+    "shasum": "b2a45da5fdfa20b0496fc3768cc27c12fa916a7d",
+    "tarball": "http://registry.npmjs.org/asap/-/asap-1.0.0.tgz"
+  },
+  "_from": "asap@>=1.0.0 <1.1.0",
+  "_npmVersion": "1.2.15",
+  "_npmUser": {
+    "name": "kriskowal",
+    "email": "kris.kowal@cixar.com"
+  },
+  "maintainers": [
+    {
+      "name": "kriskowal",
+      "email": "kris.kowal@cixar.com"
+    }
+  ],
+  "directories": {},
+  "_shasum": "b2a45da5fdfa20b0496fc3768cc27c12fa916a7d",
+  "_resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/package.json
new file mode 100644
index 0000000..17db51b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/package.json
@@ -0,0 +1,57 @@
+{
+  "name": "promise",
+  "version": "6.1.0",
+  "description": "Bare bones Promises/A+ implementation",
+  "main": "index.js",
+  "scripts": {
+    "test": "mocha --timeout 200 --slow 99999",
+    "test-resolve": "mocha test/resolver-tests.js -R spec --timeout 200 --slow 999999",
+    "test-extensions": "mocha test/extensions-tests.js -R spec --timeout 200 --slow 999999"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/then/promise.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "devDependencies": {
+    "promises-aplus-tests": "*",
+    "better-assert": "*",
+    "mocha": "*"
+  },
+  "dependencies": {
+    "asap": "~1.0.0"
+  },
+  "gitHead": "c627c0cc1f886ed3aa26edec97838ec9e7dd858b",
+  "bugs": {
+    "url": "https://github.com/then/promise/issues"
+  },
+  "homepage": "https://github.com/then/promise",
+  "_id": "promise@6.1.0",
+  "_shasum": "2ce729f6b94b45c26891ad0602c5c90e04c6eef6",
+  "_from": "promise@>=6.0.1 <7.0.0",
+  "_npmVersion": "1.5.0-alpha-4",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    },
+    {
+      "name": "nathan7",
+      "email": "nathan@nathan7.eu"
+    }
+  ],
+  "dist": {
+    "shasum": "2ce729f6b94b45c26891ad0602c5c90e04c6eef6",
+    "tarball": "http://registry.npmjs.org/promise/-/promise-6.1.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/polyfill-done.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/polyfill-done.js
new file mode 100644
index 0000000..e50b4c0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/polyfill-done.js
@@ -0,0 +1,12 @@
+// should work in any browser without browserify
+
+if (typeof Promise.prototype.done !== 'function') {
+  Promise.prototype.done = function (onFulfilled, onRejected) {
+    var self = arguments.length ? this.then.apply(this, arguments) : this
+    self.then(null, function (err) {
+      setTimeout(function () {
+        throw err
+      }, 0)
+    })
+  }
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/polyfill.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/polyfill.js
new file mode 100644
index 0000000..db099f8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/node_modules/promise/polyfill.js
@@ -0,0 +1,10 @@
+// not "use strict" so we can declare global "Promise"
+
+var asap = require('asap');
+
+if (typeof Promise === 'undefined') {
+  Promise = require('./lib/core.js')
+  require('./lib/es6-extensions.js')
+}
+
+require('./polyfill-done.js');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/package.json
new file mode 100644
index 0000000..7d9e837
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/jstransformer/package.json
@@ -0,0 +1,61 @@
+{
+  "name": "jstransformer",
+  "version": "0.0.2",
+  "description": "Normalize the API of any jstransformer",
+  "keywords": [
+    "jstransformer"
+  ],
+  "dependencies": {
+    "is-promise": "^2.0.0",
+    "promise": "^6.0.1"
+  },
+  "devDependencies": {
+    "coveralls": "^2.11.2",
+    "istanbul": "^0.3.5",
+    "testit": "^1.2.0"
+  },
+  "scripts": {
+    "test": "node test",
+    "coverage": "istanbul cover test",
+    "coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls"
+  },
+  "files": [
+    "index.js",
+    "LICENSE"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jstransformers/jstransformer.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "99b40c1aa9fa984585aa50f4618d97a0287495c1",
+  "bugs": {
+    "url": "https://github.com/jstransformers/jstransformer/issues"
+  },
+  "homepage": "https://github.com/jstransformers/jstransformer",
+  "_id": "jstransformer@0.0.2",
+  "_shasum": "7aae29a903d196cfa0973d885d3e47947ecd76ab",
+  "_from": "jstransformer@0.0.2",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "7aae29a903d196cfa0973d885d3e47947ecd76ab",
+    "tarball": "http://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/.travis.yml
new file mode 100644
index 0000000..895dbd3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+  - 0.6
+  - 0.8
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/LICENSE
new file mode 100644
index 0000000..ee27ba4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/LICENSE
@@ -0,0 +1,18 @@
+This software is released under the MIT license:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/example/async.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/example/async.js
new file mode 100644
index 0000000..6624ff7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/example/async.js
@@ -0,0 +1,5 @@
+var resolve = require('../');
+resolve('tap', { basedir: __dirname }, function (err, res) {
+    if (err) console.error(err)
+    else console.log(res)
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/example/sync.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/example/sync.js
new file mode 100644
index 0000000..54b2cc1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/example/sync.js
@@ -0,0 +1,3 @@
+var resolve = require('../');
+var res = resolve.sync('tap', { basedir: __dirname });
+console.log(res);
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/index.js
new file mode 100644
index 0000000..51f194b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/index.js
@@ -0,0 +1,5 @@
+var core = require('./lib/core');
+exports = module.exports = require('./lib/async');
+exports.core = core;
+exports.isCore = function (x) { return core[x] };
+exports.sync = require('./lib/sync');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/async.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/async.js
new file mode 100644
index 0000000..0f0eeca
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/async.js
@@ -0,0 +1,192 @@
+var core = require('./core');
+var fs = require('fs');
+var path = require('path');
+var caller = require('./caller.js');
+var nodeModulesPaths = require('./node-modules-paths.js');
+var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;
+
+module.exports = function resolve (x, opts, cb) {
+    if (typeof opts === 'function') {
+        cb = opts;
+        opts = {};
+    }
+    if (!opts) opts = {};
+    if (typeof x !== 'string') {
+        return process.nextTick(function () {
+            cb(new Error('path must be a string'));
+        });
+    }
+    
+    var isFile = opts.isFile || function (file, cb) {
+        fs.stat(file, function (err, stat) {
+            if (err && err.code === 'ENOENT') cb(null, false)
+            else if (err) cb(err)
+            else cb(null, stat.isFile() || stat.isFIFO())
+        });
+    };
+    var readFile = opts.readFile || fs.readFile;
+    
+    var extensions = opts.extensions || [ '.js' ];
+    var y = opts.basedir || path.dirname(caller());
+    
+    opts.paths = opts.paths || [];
+    
+    if (/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/.test(x)) {
+        var res = path.resolve(y, x);
+        if (x === '..') res += '/';
+        if (/\/$/.test(x) && res === y) {
+            loadAsDirectory(res, opts.package, onfile);
+        }
+        else loadAsFile(res, opts.package, onfile);
+    }
+    else loadNodeModules(x, y, function (err, n, pkg) {
+        if (err) cb(err)
+        else if (n) cb(null, n, pkg)
+        else if (core[x]) return cb(null, x);
+        else cb(new Error("Cannot find module '" + x + "' from '" + y + "'"))
+    });
+    
+    function onfile (err, m, pkg) {
+        if (err) cb(err)
+        else if (m) cb(null, m, pkg)
+        else loadAsDirectory(res, function (err, d, pkg) {
+            if (err) cb(err)
+            else if (d) cb(null, d, pkg)
+            else cb(new Error("Cannot find module '" + x + "' from '" + y + "'"))
+        })
+    }
+    
+    function loadAsFile (x, pkg, cb) {
+        if (typeof pkg === 'function') {
+            cb = pkg;
+            pkg = undefined;
+        }
+        
+        var exts = [''].concat(extensions);
+        load(exts, x, pkg)
+		
+		function load (exts, x, pkg) {
+            if (exts.length === 0) return cb(null, undefined, pkg);
+            var file = x + exts[0];
+            
+            if (pkg) onpkg(null, pkg)
+            else loadpkg(path.dirname(file), onpkg);
+            
+            function onpkg (err, pkg_, dir) {
+                pkg = pkg_;
+                if (err) return cb(err)
+                if (dir && pkg && opts.pathFilter) {
+                    var rfile = path.relative(dir, file);
+                    var rel = rfile.slice(0, rfile.length - exts[0].length);
+                    var r = opts.pathFilter(pkg, x, rel);
+                    if (r) return load(
+                        [''].concat(extensions.slice()),
+                        path.resolve(dir, r),
+                        pkg
+                    );
+                }
+                isFile(file, onex);
+            }
+            function onex (err, ex) {
+                if (err) cb(err)
+                else if (!ex) load(exts.slice(1), x, pkg)
+                else cb(null, file, pkg)
+            }
+        }
+    }
+    
+    function loadpkg (dir, cb) {
+        if (dir === '' || dir === '/') return cb(null);
+        if (process.platform === 'win32' && /^\w:[\\\/]*$/.test(dir)) {
+            return cb(null);
+        }
+        if (/[\\\/]node_modules[\\\/]*$/.test(dir)) return cb(null);
+        
+        var pkgfile = path.join(dir, 'package.json');
+        isFile(pkgfile, function (err, ex) {
+            // on err, ex is false
+            if (!ex) return loadpkg(
+                path.dirname(dir), cb
+            );
+            
+            readFile(pkgfile, function (err, body) {
+                if (err) cb(err);
+                try { var pkg = JSON.parse(body) }
+                catch (err) {}
+                
+                if (pkg && opts.packageFilter) {
+                    pkg = opts.packageFilter(pkg, pkgfile);
+                }
+                cb(null, pkg, dir);
+            });
+        });
+    }
+    
+    function loadAsDirectory (x, fpkg, cb) {
+        if (typeof fpkg === 'function') {
+            cb = fpkg;
+            fpkg = opts.package;
+        }
+        
+        var pkgfile = path.join(x, '/package.json');
+        isFile(pkgfile, function (err, ex) {
+            if (err) return cb(err);
+            if (!ex) return loadAsFile(path.join(x, '/index'), fpkg, cb);
+            
+            readFile(pkgfile, function (err, body) {
+                if (err) return cb(err);
+                try {
+                    var pkg = JSON.parse(body);
+                }
+                catch (err) {}
+                
+                if (opts.packageFilter) {
+                    pkg = opts.packageFilter(pkg, pkgfile);
+                }
+                
+                if (pkg.main) {
+                    if (pkg.main === '.' || pkg.main === './'){
+                        pkg.main = 'index'
+                    }
+                    loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) {
+                        if (err) return cb(err);
+                        if (m) return cb(null, m, pkg);
+                        if (!pkg) return loadAsFile(path.join(x, '/index'), pkg, cb);
+
+                        var dir = path.resolve(x, pkg.main);
+                        loadAsDirectory(dir, pkg, function (err, n, pkg) {
+                            if (err) return cb(err);
+                            if (n) return cb(null, n, pkg);
+                            loadAsFile(path.join(x, '/index'), pkg, cb);
+                        });
+                    });
+                    return;
+                }
+                
+                loadAsFile(path.join(x, '/index'), pkg, cb);
+            });
+        });
+    }
+    
+    function loadNodeModules (x, start, cb) {
+        (function process (dirs) {
+            if (dirs.length === 0) return cb(null, undefined);
+            var dir = dirs[0];
+            
+            var file = path.join(dir, '/', x);
+            loadAsFile(file, undefined, onfile);
+            
+            function onfile (err, m, pkg) {
+                if (err) return cb(err);
+                if (m) return cb(null, m, pkg);
+                loadAsDirectory(path.join(dir, '/', x), undefined, ondir);
+            }
+            
+            function ondir (err, n, pkg) {
+                if (err) return cb(err);
+                if (n) return cb(null, n, pkg);
+                process(dirs.slice(1));
+            }
+        })(nodeModulesPaths(start, opts));
+    }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/caller.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/caller.js
new file mode 100644
index 0000000..5536549
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/caller.js
@@ -0,0 +1,8 @@
+module.exports = function () {
+    // see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
+    var origPrepareStackTrace = Error.prepareStackTrace;
+    Error.prepareStackTrace = function (_, stack) { return stack };
+    var stack = (new Error()).stack;
+    Error.prepareStackTrace = origPrepareStackTrace;
+    return stack[2].getFileName();
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/core.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/core.js
new file mode 100644
index 0000000..ea4a6c8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/core.js
@@ -0,0 +1,4 @@
+module.exports = require('./core.json').reduce(function (acc, x) {
+    acc[x] = true;
+    return acc;
+}, {});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/core.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/core.json
new file mode 100644
index 0000000..28560f7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/core.json
@@ -0,0 +1,38 @@
+[
+    "assert",
+    "buffer_ieee754",
+    "buffer",
+    "child_process",
+    "cluster",
+    "console",
+    "constants",
+    "crypto",
+    "_debugger",
+    "dgram",
+    "dns",
+    "domain",
+    "events",
+    "freelist",
+    "fs",
+    "http",
+    "https",
+    "_linklist",
+    "module",
+    "net",
+    "os",
+    "path",
+    "punycode",
+    "querystring",
+    "readline",
+    "repl",
+    "stream",
+    "string_decoder",
+    "sys",
+    "timers",
+    "tls",
+    "tty",
+    "url",
+    "util",
+    "vm",
+    "zlib"
+]
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/node-modules-paths.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/node-modules-paths.js
new file mode 100644
index 0000000..7c58e10
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/node-modules-paths.js
@@ -0,0 +1,36 @@
+var path = require('path');
+
+module.exports = function (start, opts) {
+    var modules = opts.moduleDirectory
+        ? [].concat(opts.moduleDirectory)
+        : ['node_modules']
+    ;
+    var prefix = '/';
+    if (/^([A-Za-z]:)/.test(start)) {
+        prefix = '';
+    } else if (/^\\\\/.test(start)) {
+        prefix = '\\\\';
+    }
+    var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\/+/;
+
+    // ensure that `start` is an absolute path at this point,
+    // resolving againt the process' current working directory
+    start = path.resolve(start);
+
+    var parts = start.split(splitRe);
+
+    var dirs = [];
+    for (var i = parts.length - 1; i >= 0; i--) {
+        if (modules.indexOf(parts[i]) !== -1) continue;
+        dirs = dirs.concat(modules.map(function(module_dir) {
+            return prefix + path.join(
+                path.join.apply(path, parts.slice(0, i + 1)),
+                module_dir
+            );
+        }));
+    }
+    if (process.platform === 'win32'){
+        dirs[dirs.length-1] = dirs[dirs.length-1].replace(":", ":\\");
+    }
+    return dirs.concat(opts.paths);
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/sync.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/sync.js
new file mode 100644
index 0000000..ef91edd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/lib/sync.js
@@ -0,0 +1,81 @@
+var core = require('./core');
+var fs = require('fs');
+var path = require('path');
+var caller = require('./caller.js');
+var nodeModulesPaths = require('./node-modules-paths.js');
+
+module.exports = function (x, opts) {
+    if (!opts) opts = {};
+    var isFile = opts.isFile || function (file) {
+        try { var stat = fs.statSync(file) }
+        catch (err) { if (err && err.code === 'ENOENT') return false }
+        return stat.isFile() || stat.isFIFO();
+    };
+    var readFileSync = opts.readFileSync || fs.readFileSync;
+    
+    var extensions = opts.extensions || [ '.js' ];
+    var y = opts.basedir || path.dirname(caller());
+
+    opts.paths = opts.paths || [];
+
+    if (/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/.test(x)) {
+        var res = path.resolve(y, x);
+        if (x === '..') res += '/';
+        var m = loadAsFileSync(res) || loadAsDirectorySync(res);
+        if (m) return m;
+    } else {
+        var n = loadNodeModulesSync(x, y);
+        if (n) return n;
+    }
+    
+    if (core[x]) return x;
+    
+    throw new Error("Cannot find module '" + x + "' from '" + y + "'");
+    
+    function loadAsFileSync (x) {
+        if (isFile(x)) {
+            return x;
+        }
+        
+        for (var i = 0; i < extensions.length; i++) {
+            var file = x + extensions[i];
+            if (isFile(file)) {
+                return file;
+            }
+        }
+    }
+    
+    function loadAsDirectorySync (x) {
+        var pkgfile = path.join(x, '/package.json');
+        if (isFile(pkgfile)) {
+            var body = readFileSync(pkgfile, 'utf8');
+            try {
+                var pkg = JSON.parse(body);
+                if (opts.packageFilter) {
+                    pkg = opts.packageFilter(pkg, x);
+                }
+                
+                if (pkg.main) {
+                    var m = loadAsFileSync(path.resolve(x, pkg.main));
+                    if (m) return m;
+                    var n = loadAsDirectorySync(path.resolve(x, pkg.main));
+                    if (n) return n;
+                }
+            }
+            catch (err) {}
+        }
+        
+        return loadAsFileSync(path.join( x, '/index'));
+    }
+    
+    function loadNodeModulesSync (x, start) {
+        var dirs = nodeModulesPaths(start, opts);
+        for (var i = 0; i < dirs.length; i++) {
+            var dir = dirs[i];
+            var m = loadAsFileSync(path.join( dir, '/', x));
+            if (m) return m;
+            var n = loadAsDirectorySync(path.join( dir, '/', x ));
+            if (n) return n;
+        }
+    }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/package.json
new file mode 100644
index 0000000..85f86d0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/package.json
@@ -0,0 +1,56 @@
+{
+  "name": "resolve",
+  "description": "resolve like require.resolve() on behalf of files asynchronously and synchronously",
+  "version": "1.1.6",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/substack/node-resolve.git"
+  },
+  "main": "index.js",
+  "keywords": [
+    "resolve",
+    "require",
+    "node",
+    "module"
+  ],
+  "scripts": {
+    "test": "tape test/*.js"
+  },
+  "devDependencies": {
+    "tape": "^3.5.0",
+    "tap": "0.4.13"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "James Halliday",
+    "email": "mail@substack.net",
+    "url": "http://substack.net"
+  },
+  "gitHead": "38d451c0ecd9267277a7683970432d37f001441e",
+  "bugs": {
+    "url": "https://github.com/substack/node-resolve/issues"
+  },
+  "homepage": "https://github.com/substack/node-resolve",
+  "_id": "resolve@1.1.6",
+  "_shasum": "d3492ad054ca800f5befa612e61beac1eec98f8f",
+  "_from": "resolve@>=1.1.6 <2.0.0",
+  "_npmVersion": "2.3.0",
+  "_nodeVersion": "0.12.0",
+  "_npmUser": {
+    "name": "substack",
+    "email": "mail@substack.net"
+  },
+  "maintainers": [
+    {
+      "name": "substack",
+      "email": "mail@substack.net"
+    }
+  ],
+  "dist": {
+    "shasum": "d3492ad054ca800f5befa612e61beac1eec98f8f",
+    "tarball": "http://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/readme.markdown b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/readme.markdown
new file mode 100644
index 0000000..4fab9b0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/readme.markdown
@@ -0,0 +1,148 @@
+# resolve
+
+implements the [node `require.resolve()`
+algorithm](http://nodejs.org/docs/v0.4.8/api/all.html#all_Together...)
+such that you can `require.resolve()` on behalf of a file asynchronously and
+synchronously
+
+[![build status](https://secure.travis-ci.org/substack/node-resolve.png)](http://travis-ci.org/substack/node-resolve)
+
+# example
+
+asynchronously resolve:
+
+``` js
+var resolve = require('resolve');
+resolve('tap', { basedir: __dirname }, function (err, res) {
+    if (err) console.error(err)
+    else console.log(res)
+});
+```
+
+```
+$ node example/async.js
+/home/substack/projects/node-resolve/node_modules/tap/lib/main.js
+```
+
+synchronously resolve:
+
+``` js
+var resolve = require('resolve');
+var res = resolve.sync('tap', { basedir: __dirname });
+console.log(res);
+```
+
+```
+$ node example/sync.js
+/home/substack/projects/node-resolve/node_modules/tap/lib/main.js
+```
+
+# methods
+
+``` js
+var resolve = require('resolve')
+```
+
+## resolve(id, opts={}, cb)
+
+Asynchronously resolve the module path string `id` into `cb(err, res [, pkg])`, where `pkg` (if defined) is the data from `package.json`.
+
+options are:
+
+* opts.basedir - directory to begin resolving from
+
+* opts.package - `package.json` data applicable to the module being loaded
+
+* opts.extensions - array of file extensions to search in order
+
+* opts.readFile - how to read files asynchronously
+
+* opts.isFile - function to asynchronously test whether a file exists
+
+* opts.packageFilter - transform the parsed package.json contents before looking
+at the "main" field
+
+* opts.pathFilter(pkg, path, relativePath) - transform a path within a package
+  * pkg - package data
+  * path - the path being resolved
+  * relativePath - the path relative from the package.json location
+  * returns - a relative path that will be joined from the package.json location
+
+* opts.paths - require.paths array to use if nothing is found on the normal
+node_modules recursive walk (probably don't use this)
+
+* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
+
+default `opts` values:
+
+``` javascript
+{
+    paths: [],
+    basedir: __dirname,
+    extensions: [ '.js' ],
+    readFile: fs.readFile,
+    isFile: function (file, cb) {
+        fs.stat(file, function (err, stat) {
+            if (err && err.code === 'ENOENT') cb(null, false)
+            else if (err) cb(err)
+            else cb(null, stat.isFile())
+        });
+    },
+    moduleDirectory: 'node_modules'
+}
+```
+
+## resolve.sync(id, opts)
+
+Synchronously resolve the module path string `id`, returning the result and
+throwing an error when `id` can't be resolved.
+
+options are:
+
+* opts.basedir - directory to begin resolving from
+
+* opts.extensions - array of file extensions to search in order
+
+* opts.readFile - how to read files synchronously
+
+* opts.isFile - function to synchronously test whether a file exists
+
+* `opts.packageFilter(pkg, pkgfile)` - transform the parsed package.json
+* contents before looking at the "main" field
+
+* opts.paths - require.paths array to use if nothing is found on the normal
+node_modules recursive walk (probably don't use this)
+
+* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"`
+
+default `opts` values:
+
+``` javascript
+{
+    paths: [],
+    basedir: __dirname,
+    extensions: [ '.js' ],
+    readFileSync: fs.readFileSync,
+    isFile: function (file) {
+        try { return fs.statSync(file).isFile() }
+        catch (e) { return false }
+    },
+    moduleDirectory: 'node_modules'
+}
+````
+
+## resolve.isCore(pkg)
+
+Return whether a package is in core.
+
+# install
+
+With [npm](https://npmjs.org) do:
+
+```
+npm install resolve
+```
+
+# license
+
+MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/core.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/core.js
new file mode 100644
index 0000000..4a56682
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/core.js
@@ -0,0 +1,12 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('core modules', function (t) {
+    t.ok(resolve.isCore('fs'));
+    t.ok(resolve.isCore('net'));
+    t.ok(resolve.isCore('http'));
+    
+    t.ok(!resolve.isCore('seq'));
+    t.ok(!resolve.isCore('../'));
+    t.end();
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot.js
new file mode 100644
index 0000000..b876772
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot.js
@@ -0,0 +1,29 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('dotdot', function (t) {
+    t.plan(4);
+    var dir = __dirname + '/dotdot/abc';
+    
+    resolve('..', { basedir : dir }, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, __dirname + '/dotdot/index.js');
+    });
+    
+    resolve('.', { basedir : dir }, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, dir + '/index.js');
+    });
+});
+
+test('dotdot sync', function (t) {
+    t.plan(2);
+    var dir = __dirname + '/dotdot/abc';
+    
+    var a = resolve.sync('..', { basedir : dir });
+    t.equal(a, __dirname + '/dotdot/index.js');
+    
+    var b = resolve.sync('.', { basedir : dir });
+    t.equal(b, dir + '/index.js');
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot/abc/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot/abc/index.js
new file mode 100644
index 0000000..67f2534
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot/abc/index.js
@@ -0,0 +1,2 @@
+var x = require('..');
+console.log(x);
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot/index.js
new file mode 100644
index 0000000..afec736
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/dotdot/index.js
@@ -0,0 +1 @@
+module.exports = 'whatever'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/faulty_basedir.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/faulty_basedir.js
new file mode 100644
index 0000000..2440818
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/faulty_basedir.js
@@ -0,0 +1,17 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+// not sure what's up with this test anymore
+if (process.platform !== 'win32') return;
+
+test('faulty basedir must produce error in windows', function (t) {
+    t.plan(1);
+
+    var resolverDir = 'C:\\a\\b\\c\\d';
+
+    resolve('tape/lib/test.js', { basedir : resolverDir }, function (err, res, pkg) {
+        t.equal(true, !!err);
+    });
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/filter.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/filter.js
new file mode 100644
index 0000000..07c38f3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/filter.js
@@ -0,0 +1,18 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('filter', function (t) {
+    t.plan(2);
+    var dir = __dirname + '/resolver';
+    resolve('./baz', {
+        basedir : dir,
+        packageFilter : function (pkg) {
+            pkg.main = 'doom';
+            return pkg;
+        }
+    }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/baz/doom.js');
+        t.equal(pkg.main, 'doom');
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/filter_sync.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/filter_sync.js
new file mode 100644
index 0000000..3f89b79
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/filter_sync.js
@@ -0,0 +1,15 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('filter', function (t) {
+    var dir = __dirname + '/resolver';
+    var res = resolve.sync('./baz', {
+        basedir : dir,
+        packageFilter : function (pkg) {
+            pkg.main = 'doom'
+            return pkg;
+        }
+    });
+    t.equal(res, dir + '/baz/doom.js');
+    t.end();
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/mock.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/mock.js
new file mode 100644
index 0000000..1cf3b12
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/mock.js
@@ -0,0 +1,142 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('mock', function (t) {
+    t.plan(6);
+    
+    var files = {
+        '/foo/bar/baz.js' : 'beep'
+    };
+    
+    function opts (basedir) {
+        return {
+            basedir : basedir,
+            isFile : function (file, cb) {
+                cb(null, files.hasOwnProperty(file));
+            },
+            readFile : function (file, cb) {
+                cb(null, files[file]);
+            }
+        }
+    }
+    
+    resolve('./baz', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, '/foo/bar/baz.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, '/foo/bar/baz.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module 'baz' from '/foo/bar'");
+    });
+    
+    resolve('../baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module '../baz' from '/foo/bar'");
+    });
+});
+
+test('mock from package', function (t) {
+    t.plan(6);
+    
+    var files = {
+        '/foo/bar/baz.js' : 'beep'
+    };
+    
+    function opts (basedir) {
+        return {
+            basedir : basedir,
+            package : { main: 'bar' },
+            isFile : function (file, cb) {
+                cb(null, files.hasOwnProperty(file));
+            },
+            readFile : function (file, cb) {
+                cb(null, files[file]);
+            }
+        }
+    }
+    
+    resolve('./baz', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, '/foo/bar/baz.js');
+        t.equal(pkg.main, 'bar');
+    });
+    
+    resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, '/foo/bar/baz.js');
+        t.equal(pkg.main, 'bar');
+    });
+    
+    resolve('baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module 'baz' from '/foo/bar'");
+    });
+    
+    resolve('../baz', opts('/foo/bar'), function (err, res) {
+        t.equal(err.message, "Cannot find module '../baz' from '/foo/bar'");
+    });
+});
+
+test('mock package', function (t) {
+    t.plan(2);
+    
+    var files = {
+        '/foo/node_modules/bar/baz.js' : 'beep',
+        '/foo/node_modules/bar/package.json' : JSON.stringify({
+            main : './baz.js'
+        })
+    };
+    
+    function opts (basedir) {
+        return {
+            basedir : basedir,
+            isFile : function (file, cb) {
+                cb(null, files.hasOwnProperty(file));
+            },
+            readFile : function (file, cb) {
+                cb(null, files[file]);
+            }
+        }
+    }
+    
+    resolve('bar', opts('/foo'), function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, '/foo/node_modules/bar/baz.js');
+        t.equal(pkg.main, './baz.js');
+    });
+});
+
+test('mock package from package', function (t) {
+    t.plan(2);
+    
+    var files = {
+        '/foo/node_modules/bar/baz.js' : 'beep',
+        '/foo/node_modules/bar/package.json' : JSON.stringify({
+            main : './baz.js'
+        })
+    };
+    
+    function opts (basedir) {
+        return {
+            basedir : basedir,
+            package : { main: 'bar' },
+            isFile : function (file, cb) {
+                cb(null, files.hasOwnProperty(file));
+            },
+            readFile : function (file, cb) {
+                cb(null, files[file]);
+            }
+        }
+    }
+    
+    resolve('bar', opts('/foo'), function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, '/foo/node_modules/bar/baz.js');
+        t.equal(pkg.main, './baz.js');
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/mock_sync.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/mock_sync.js
new file mode 100644
index 0000000..abfd289
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/mock_sync.js
@@ -0,0 +1,68 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('mock', function (t) {
+    t.plan(4);
+    
+    var files = {
+        '/foo/bar/baz.js' : 'beep'
+    };
+    
+    function opts (basedir) {
+        return {
+            basedir : basedir,
+            isFile : function (file) {
+                return files.hasOwnProperty(file)
+            },
+            readFileSync : function (file) {
+                return files[file]
+            }
+        }
+    }
+    
+    t.equal(
+        resolve.sync('./baz', opts('/foo/bar')),
+        '/foo/bar/baz.js'
+    );
+    
+    t.equal(
+        resolve.sync('./baz.js', opts('/foo/bar')),
+        '/foo/bar/baz.js'
+    );
+    
+    t.throws(function () {
+        resolve.sync('baz', opts('/foo/bar'));
+    });
+
+    t.throws(function () {
+        resolve.sync('../baz', opts('/foo/bar'));
+    });
+});
+
+test('mock package', function (t) {
+    t.plan(1);
+    
+    var files = {
+        '/foo/node_modules/bar/baz.js' : 'beep',
+        '/foo/node_modules/bar/package.json' : JSON.stringify({
+            main : './baz.js'
+        })
+    };
+    
+    function opts (basedir) {
+        return {
+            basedir : basedir,
+            isFile : function (file) {
+                return files.hasOwnProperty(file)
+            },
+            readFileSync : function (file) {
+                return files[file]
+            }
+        }
+    }
+    
+    t.equal(
+        resolve.sync('bar', opts('/foo')),
+        '/foo/node_modules/bar/baz.js'
+    );
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir.js
new file mode 100644
index 0000000..06395d8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir.js
@@ -0,0 +1,56 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('moduleDirectory strings', function (t) {
+    t.plan(4);
+    var dir = __dirname + '/module_dir';
+    var xopts = {
+        basedir : dir,
+        moduleDirectory: 'xmodules'
+    };
+    resolve('aaa', xopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, dir + '/xmodules/aaa/index.js');
+    });
+    
+    var yopts = {
+        basedir : dir,
+        moduleDirectory: 'ymodules'
+    };
+    resolve('aaa', yopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, dir + '/ymodules/aaa/index.js');
+    });
+});
+
+test('moduleDirectory array', function (t) {
+    t.plan(6);
+    var dir = __dirname + '/module_dir';
+    var aopts = {
+        basedir : dir,
+        moduleDirectory: [ 'xmodules', 'ymodules', 'zmodules' ]
+    };
+    resolve('aaa', aopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, dir + '/xmodules/aaa/index.js');
+    });
+    
+    var bopts = {
+        basedir : dir,
+        moduleDirectory: [ 'zmodules', 'ymodules', 'xmodules' ]
+    };
+    resolve('aaa', bopts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, dir + '/ymodules/aaa/index.js');
+    });
+    
+    var copts = {
+        basedir : dir,
+        moduleDirectory: [ 'xmodules', 'ymodules', 'zmodules' ]
+    };
+    resolve('bbb', copts, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, dir + '/zmodules/bbb/main.js');
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/xmodules/aaa/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/xmodules/aaa/index.js
new file mode 100644
index 0000000..55cd18c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/xmodules/aaa/index.js
@@ -0,0 +1 @@
+module.exports = function (x) { return x * 100 }
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/ymodules/aaa/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/ymodules/aaa/index.js
new file mode 100644
index 0000000..651aca8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/ymodules/aaa/index.js
@@ -0,0 +1 @@
+module.exports = function (x) { return x + 100 }
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/zmodules/bbb/main.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/zmodules/bbb/main.js
new file mode 100644
index 0000000..4325a0b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/zmodules/bbb/main.js
@@ -0,0 +1 @@
+module.exports = function (n) { return n * 111 }
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/zmodules/bbb/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/zmodules/bbb/package.json
new file mode 100644
index 0000000..c13b8cf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/module_dir/zmodules/bbb/package.json
@@ -0,0 +1,3 @@
+{
+  "main": "main.js"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path.js
new file mode 100644
index 0000000..2407189
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path.js
@@ -0,0 +1,48 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('$NODE_PATH', function (t) {
+    t.plan(4);
+    
+    resolve('aaa', {
+        paths: [
+            __dirname + '/node_path/x',
+            __dirname + '/node_path/y'
+        ],
+        basedir: __dirname,
+    }, function (err, res) {
+        t.equal(res, __dirname + '/node_path/x/aaa/index.js');
+    });
+    
+    resolve('bbb', {
+        paths: [
+            __dirname + '/node_path/x',
+            __dirname + '/node_path/y'
+        ],
+        basedir: __dirname,
+    }, function (err, res) {
+        t.equal(res, __dirname + '/node_path/y/bbb/index.js');
+    });
+    
+    resolve('ccc', {
+        paths: [
+            __dirname + '/node_path/x',
+            __dirname + '/node_path/y'
+        ],
+        basedir: __dirname,
+    }, function (err, res) {
+        t.equal(res, __dirname + '/node_path/x/ccc/index.js');
+    });
+
+    // ensure that relative paths still resolve against the
+    // regular `node_modules` correctly
+    resolve('tap', {
+        paths: [
+            'node_path',
+        ],
+        basedir: 'node_path/x',
+    }, function (err, res) {
+        t.equal(res, path.resolve(__dirname, '..', 'node_modules/tap/lib/main.js'));
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/x/aaa/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/x/aaa/index.js
new file mode 100644
index 0000000..1ea5913
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/x/aaa/index.js
@@ -0,0 +1 @@
+module.exports = 'A'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/x/ccc/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/x/ccc/index.js
new file mode 100644
index 0000000..f186fa7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/x/ccc/index.js
@@ -0,0 +1 @@
+module.exports = 'C'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/y/bbb/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/y/bbb/index.js
new file mode 100644
index 0000000..e22dd83
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/y/bbb/index.js
@@ -0,0 +1 @@
+module.exports = 'B'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/y/ccc/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/y/ccc/index.js
new file mode 100644
index 0000000..d0043d1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/node_path/y/ccc/index.js
@@ -0,0 +1 @@
+module.exports = 'CY'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/nonstring.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/nonstring.js
new file mode 100644
index 0000000..ef63c40
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/nonstring.js
@@ -0,0 +1,9 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('nonstring', function (t) {
+    t.plan(1);
+    resolve(555, function (err, res, pkg) {
+        t.ok(err);
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter.js
new file mode 100644
index 0000000..142f94d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter.js
@@ -0,0 +1,35 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('#62: deep module references and the pathFilter', function(t){
+	t.plan(9);
+    
+	var resolverDir = __dirname + '/pathfilter/deep_ref';
+	var pathFilter = function(pkg, x, remainder){
+		t.equal(pkg.version, "1.2.3");
+		t.equal(x, resolverDir + '/node_modules/deep/ref');
+		t.equal(remainder, "ref");
+		return "alt";
+	};
+	
+	resolve('deep/ref', { basedir : resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+
+        t.equal(pkg.version, "1.2.3");
+        t.equal(res, resolverDir + '/node_modules/deep/ref.js');
+    });
+
+    resolve('deep/deeper/ref', { basedir: resolverDir },
+    function(err, res, pkg) {
+      if(err) t.fail(err);
+      t.notEqual(pkg, undefined);
+      t.equal(pkg.version, "1.2.3");
+      t.equal(res, resolverDir + '/node_modules/deep/deeper/ref.js');
+    });
+    
+    resolve('deep/ref', { basedir : resolverDir, pathFilter : pathFilter },
+    function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, resolverDir + '/node_modules/deep/alt.js');
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/main.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/main.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/alt.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/alt.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/deeper/ref.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/deeper/ref.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/package.json
new file mode 100644
index 0000000..fe4b408
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "deep",
+  "version": "1.2.3"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/ref.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/pathfilter/deep_ref/node_modules/deep/ref.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence.js
new file mode 100644
index 0000000..c716f0e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence.js
@@ -0,0 +1,23 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('precedence', function (t) {
+    t.plan(3);
+    var dir = path.join(__dirname, 'precedence/aaa');
+    
+    resolve('./', { basedir : dir }, function (err, res, pkg) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, 'index.js'));
+        t.equal(pkg.name, 'resolve');
+    });
+});
+
+test('./ should not load ${dir}.js', function (t) {
+    t.plan(1);
+    var dir = path.join(__dirname, 'precedence/bbb');
+    
+    resolve('./', { basedir : dir }, function (err, res, pkg) {
+        t.ok(err);
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa.js
new file mode 100644
index 0000000..a182397
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa.js
@@ -0,0 +1 @@
+module.exports = 'wtf'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa/index.js
new file mode 100644
index 0000000..993b03c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa/index.js
@@ -0,0 +1 @@
+module.exports = 'okok'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa/main.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa/main.js
new file mode 100644
index 0000000..db38959
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/aaa/main.js
@@ -0,0 +1 @@
+console.log(require('./'))
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/bbb.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/bbb.js
new file mode 100644
index 0000000..c8a9996
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/bbb.js
@@ -0,0 +1 @@
+module.exports '>_<'
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/bbb/main.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/bbb/main.js
new file mode 100644
index 0000000..716b81d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/precedence/bbb/main.js
@@ -0,0 +1 @@
+console.log(require('./')); // should throw
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver.js
new file mode 100644
index 0000000..5bbb05f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver.js
@@ -0,0 +1,281 @@
+var path = require('path');
+var test = require('tape');
+var resolve = require('../');
+
+test('async foo', function (t) {
+    t.plan(9);
+    var dir = __dirname + '/resolver';
+    
+    resolve('./foo', { basedir : dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/foo.js');
+        t.equal(pkg.name, 'resolve');
+    });
+    
+    resolve('./foo.js', { basedir : dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/foo.js');
+        t.equal(pkg.name, 'resolve');
+    });
+    
+    resolve('./foo', { basedir : dir, package: { main: 'resolver' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/foo.js');
+        t.equal(pkg.main, 'resolver');
+    });
+    
+    resolve('./foo.js', { basedir : dir, package: { main: 'resolver' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/foo.js');
+        t.equal(pkg.main, 'resolver');
+    });
+    
+    resolve('foo', { basedir : dir }, function (err) {
+        t.equal(err.message, "Cannot find module 'foo' from '" + path.resolve(dir) + "'");
+    });
+});
+
+test('bar', function (t) {
+    t.plan(6);
+    var dir = __dirname + '/resolver';
+    
+    resolve('foo', { basedir : dir + '/bar' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/bar/node_modules/foo/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('foo', { basedir : dir + '/bar' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/bar/node_modules/foo/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('foo', { basedir : dir + '/bar', package: { main: 'bar' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/bar/node_modules/foo/index.js');
+        t.equal(pkg, undefined);
+    });
+});
+
+test('baz', function (t) {
+    t.plan(4);
+    var dir = __dirname + '/resolver';
+    
+    resolve('./baz', { basedir : dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/baz/quux.js');
+        t.equal(pkg.main, 'quux.js');
+    });
+    
+    resolve('./baz', { basedir : dir, package: { main: 'resolver' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/baz/quux.js');
+        t.equal(pkg.main, 'quux.js');
+    });
+});
+
+test('biz', function (t) {
+    t.plan(24);
+    var dir = __dirname + '/resolver/biz/node_modules';
+    
+    resolve('./grux', { basedir : dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/grux/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('./grux', { basedir : dir, package: { main: 'biz' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/grux/index.js');
+        t.equal(pkg.main, 'biz');
+    });
+    
+    resolve('./garply', { basedir : dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/garply/lib/index.js');
+        t.equal(pkg.main, './lib');
+    });
+    
+    resolve('./garply', { basedir : dir, package: { main: 'biz' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/garply/lib/index.js');
+        t.equal(pkg.main, './lib');
+    });
+    
+    resolve('tiv', { basedir : dir + '/grux' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/tiv/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('tiv', { basedir : dir + '/grux', package: { main: 'grux' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/tiv/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('tiv', { basedir : dir + '/garply' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/tiv/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('tiv', { basedir : dir + '/garply', package: { main: './lib' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/tiv/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('grux', { basedir : dir + '/tiv' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/grux/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('grux', { basedir : dir + '/tiv', package: { main: 'tiv' }  }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/grux/index.js');
+        t.equal(pkg, undefined);
+    });
+    
+    resolve('garply', { basedir : dir + '/tiv' }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/garply/lib/index.js');
+        t.equal(pkg.main, './lib');
+    });
+    
+    resolve('garply', { basedir : dir + '/tiv', package: { main: 'tiv' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/garply/lib/index.js');
+        t.equal(pkg.main, './lib');
+    });
+});
+
+test('quux', function (t) {
+    t.plan(2);
+    var dir = __dirname + '/resolver/quux';
+    
+    resolve('./foo', { basedir : dir, package: { main: 'quux' } }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/foo/index.js');
+        t.equal(pkg.main, 'quux');
+    });
+});
+
+test('normalize', function (t) {
+    t.plan(2);
+    var dir = __dirname + '/resolver/biz/node_modules/grux';
+    
+    resolve('../grux', { basedir : dir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/index.js');
+        t.equal(pkg, undefined);
+    });
+});
+
+test('cup', function (t) {
+    t.plan(3);
+    var dir = __dirname + '/resolver';
+    
+    resolve('./cup', { basedir : dir, extensions : [ '.js', '.coffee' ] },
+    function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/cup.coffee');
+    });
+    
+    resolve('./cup.coffee', { basedir : dir }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/cup.coffee');
+    });
+    
+    resolve('./cup', { basedir : dir, extensions : [ '.js' ] },
+    function (err, res) {
+        t.equal(err.message, "Cannot find module './cup' from '" + path.resolve(dir) + "'");
+    });
+});
+
+test('mug', function (t) {
+    t.plan(3);
+    var dir = __dirname + '/resolver';
+    
+    resolve('./mug', { basedir : dir }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/mug.js');
+    });
+    
+    resolve('./mug', { basedir : dir, extensions : [ '.coffee', '.js' ] },
+    function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/mug.coffee');
+    });
+    
+    resolve('./mug', { basedir : dir, extensions : [ '.js', '.coffee' ] },
+    function (err, res) {
+        t.equal(res, dir + '/mug.js');
+    });
+});
+
+test('other path', function (t) {
+    t.plan(4);
+    var resolverDir = __dirname + '/resolver';
+    var dir = resolverDir + '/bar';
+    var otherDir = resolverDir + '/other_path';
+    
+    resolve('root', { basedir : dir, paths: [otherDir] }, function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, resolverDir + '/other_path/root.js');
+    });
+    
+    resolve('lib/other-lib', { basedir : dir, paths: [otherDir] },
+    function (err, res) {
+        if (err) t.fail(err);
+        t.equal(res, resolverDir + '/other_path/lib/other-lib.js');
+    });
+    
+    resolve('root', { basedir : dir, }, function (err, res) {
+        t.equal(err.message, "Cannot find module 'root' from '" + path.resolve(dir) + "'");
+    });
+    
+    resolve('zzz', { basedir : dir, paths: [otherDir] }, function (err, res) {
+        t.equal(err.message, "Cannot find module 'zzz' from '" + path.resolve(dir) + "'");
+    });
+});
+
+test('incorrect main', function (t) {
+    t.plan(1)
+
+    var resolverDir = __dirname + '/resolver';
+    var dir = resolverDir + '/incorrect_main';
+
+    resolve('./incorrect_main', { basedir : resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, dir + '/index.js');
+    });
+});
+
+test('without basedir', function (t) {
+    t.plan(1);
+
+    var dir = __dirname + '/resolver/without_basedir';
+    var tester = require(dir + '/main.js');
+
+    tester(t, function (err, res, pkg){
+        if (err) {
+	  t.fail(err);
+	} else {
+          t.equal(res, dir + '/node_modules/mymodule.js');
+	}
+    });
+});
+
+test('#25: node modules with the same name as node stdlib modules', function (t) {
+    t.plan(1);
+
+    var resolverDir = __dirname + '/resolver/punycode';
+
+    resolve('punycode', { basedir : resolverDir }, function (err, res, pkg) {
+        if (err) t.fail(err);
+        t.equal(res, resolverDir + '/node_modules/punycode/index.js');
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/bar/node_modules/foo/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/bar/node_modules/foo/index.js
new file mode 100644
index 0000000..bd816ea
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/bar/node_modules/foo/index.js
@@ -0,0 +1 @@
+module.exports = 1;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/doom.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/doom.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/package.json
new file mode 100644
index 0000000..6b81dcd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/package.json
@@ -0,0 +1,3 @@
+{
+    "main" : "quux.js"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/quux.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/quux.js
new file mode 100644
index 0000000..bd816ea
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/baz/quux.js
@@ -0,0 +1 @@
+module.exports = 1;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/garply/lib/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/garply/lib/index.js
new file mode 100644
index 0000000..0379e29
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/garply/lib/index.js
@@ -0,0 +1 @@
+module.exports = 'hello garply';
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/garply/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/garply/package.json
new file mode 100644
index 0000000..babaac5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/garply/package.json
@@ -0,0 +1,3 @@
+{
+    "main" : "./lib"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/grux/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/grux/index.js
new file mode 100644
index 0000000..4996055
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/grux/index.js
@@ -0,0 +1 @@
+module.exports = require('tiv') * 100;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/tiv/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/tiv/index.js
new file mode 100644
index 0000000..690aad3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/biz/node_modules/tiv/index.js
@@ -0,0 +1 @@
+module.exports = 3;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/cup.coffee b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/cup.coffee
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/cup.coffee
@@ -0,0 +1 @@
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/foo.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/foo.js
new file mode 100644
index 0000000..bd816ea
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/foo.js
@@ -0,0 +1 @@
+module.exports = 1;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/incorrect_main/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/incorrect_main/index.js
new file mode 100644
index 0000000..bc1fb0a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/incorrect_main/index.js
@@ -0,0 +1,2 @@
+// this is the actual main file 'index.js', not 'wrong.js' like the package.json would indicate
+module.exports = 1;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/incorrect_main/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/incorrect_main/package.json
new file mode 100644
index 0000000..1592ed3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/incorrect_main/package.json
@@ -0,0 +1,3 @@
+{
+    "main" : "wrong.js"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/mug.coffee b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/mug.coffee
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/mug.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/mug.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/other_path/lib/other-lib.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/other_path/lib/other-lib.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/other_path/root.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/other_path/root.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/punycode/node_modules/punycode/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/punycode/node_modules/punycode/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/quux/foo/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/quux/foo/index.js
new file mode 100644
index 0000000..bd816ea
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/quux/foo/index.js
@@ -0,0 +1 @@
+module.exports = 1;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/without_basedir/main.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/without_basedir/main.js
new file mode 100644
index 0000000..5f211e9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/without_basedir/main.js
@@ -0,0 +1,6 @@
+resolve = require('../../../');
+
+module.exports = function(t, cb) {
+  resolve('mymodule', null, cb);
+}
+
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/without_basedir/node_modules/mymodule.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/without_basedir/node_modules/mymodule.js
new file mode 100644
index 0000000..2b58aa4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver/without_basedir/node_modules/mymodule.js
@@ -0,0 +1 @@
+module.exports = "The tools we use have a profound (and devious!) influence on our thinking habits, and, therefore, on our thinking abilities.- E. Dijkstra"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver_sync.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver_sync.js
new file mode 100644
index 0000000..5982531
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/resolver_sync.js
@@ -0,0 +1,180 @@
+var test = require('tape');
+var resolve = require('../');
+
+test('foo', function (t) {
+    var dir = __dirname + '/resolver';
+    
+    t.equal(
+        resolve.sync('./foo', { basedir : dir }),
+        dir + '/foo.js'
+    );
+    
+    t.equal(
+        resolve.sync('./foo.js', { basedir : dir }),
+        dir + '/foo.js'
+    );
+    
+    t.throws(function () {
+        resolve.sync('foo', { basedir : dir });
+    });
+    
+    t.end();
+});
+
+test('bar', function (t) {
+    var dir = __dirname + '/resolver';
+    
+    t.equal(
+        resolve.sync('foo', { basedir : dir + '/bar' }),
+        dir + '/bar/node_modules/foo/index.js'
+    );
+    t.end();
+});
+
+test('baz', function (t) {
+    var dir = __dirname + '/resolver';
+    
+    t.equal(
+        resolve.sync('./baz', { basedir : dir }),
+        dir + '/baz/quux.js'
+    );
+    t.end();
+});
+
+test('biz', function (t) {
+    var dir = __dirname + '/resolver/biz/node_modules';
+    t.equal(
+        resolve.sync('./grux', { basedir : dir }),
+        dir + '/grux/index.js'
+    );
+    
+    t.equal(
+        resolve.sync('tiv', { basedir : dir + '/grux' }),
+        dir + '/tiv/index.js'
+    );
+    
+    t.equal(
+        resolve.sync('grux', { basedir : dir + '/tiv' }),
+        dir + '/grux/index.js'
+    );
+    t.end();
+});
+
+test('normalize', function (t) {
+    var dir = __dirname + '/resolver/biz/node_modules/grux';
+    t.equal(
+        resolve.sync('../grux', { basedir : dir }),
+        dir + '/index.js'
+    );
+    t.end();
+});
+
+test('cup', function (t) {
+    var dir = __dirname + '/resolver';
+    t.equal(
+        resolve.sync('./cup', {
+            basedir : dir,
+            extensions : [ '.js', '.coffee' ]
+        }),
+        dir + '/cup.coffee'
+    );
+    
+    t.equal(
+        resolve.sync('./cup.coffee', {
+            basedir : dir
+        }),
+        dir + '/cup.coffee'
+    );
+    
+    t.throws(function () {
+        resolve.sync('./cup', {
+            basedir : dir,
+            extensions : [ '.js' ]
+        })
+    });
+    
+    t.end();
+});
+
+test('mug', function (t) {
+    var dir = __dirname + '/resolver';
+    t.equal(
+        resolve.sync('./mug', { basedir : dir }),
+        dir + '/mug.js'
+    );
+    
+    t.equal(
+        resolve.sync('./mug', {
+            basedir : dir,
+            extensions : [ '.coffee', '.js' ]
+        }),
+        dir + '/mug.coffee'
+    );
+    
+    t.equal(
+        resolve.sync('./mug', {
+            basedir : dir,
+            extensions : [ '.js', '.coffee' ]
+        }),
+        dir + '/mug.js'
+    );
+    
+    t.end();
+});
+
+test('other path', function (t) {
+    var resolverDir = __dirname + '/resolver';
+    var dir = resolverDir + '/bar';
+    var otherDir = resolverDir + '/other_path';
+
+    var path = require('path');
+    
+    t.equal(
+        resolve.sync('root', {
+            basedir : dir,
+            paths: [otherDir] }),
+        resolverDir + '/other_path/root.js'
+    );
+    
+    t.equal(
+        resolve.sync('lib/other-lib', {
+            basedir : dir,
+            paths: [otherDir] }),
+        resolverDir + '/other_path/lib/other-lib.js'
+    );
+
+    t.throws(function () {
+        resolve.sync('root', { basedir : dir, });
+    });
+    
+    t.throws(function () {
+        resolve.sync('zzz', {
+            basedir : dir,
+            paths: [otherDir] });
+    });
+    
+    t.end();
+});
+
+test('incorrect main', function (t) {
+    var resolverDir = __dirname + '/resolver';
+    var dir = resolverDir + '/incorrect_main';
+
+    t.equal(
+        resolve.sync('./incorrect_main', { basedir : resolverDir }),
+        dir + '/index.js'
+    )
+
+    t.end()
+});
+
+test('#25: node modules with the same name as node stdlib modules', function (t) {
+    var resolverDir = __dirname + '/resolver/punycode';
+
+    t.equal(
+        resolve.sync('punycode', { basedir : resolverDir }),
+        resolverDir + '/node_modules/punycode/index.js'
+    )
+
+    t.end()
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs.js
new file mode 100644
index 0000000..957abfe
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs.js
@@ -0,0 +1,13 @@
+var test = require('tape');
+var resolve = require('../');
+var path = require('path');
+
+test('subdirs', function (t) {
+    t.plan(2);
+    
+    var dir = path.join(__dirname, '/subdirs');
+    resolve('a/b/c/x.json', { basedir: dir }, function (err, res) {
+        t.ifError(err);
+        t.equal(res, path.join(dir, 'node_modules/a/b/c/x.json'));
+    });
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json
new file mode 100644
index 0000000..3cc0ecb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json
@@ -0,0 +1 @@
+[1,2,3]
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs/node_modules/a/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs/node_modules/a/package.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/resolve/test/subdirs/node_modules/a/package.json
@@ -0,0 +1 @@
+{}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/LICENSE
new file mode 100644
index 0000000..dd7706f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/LICENSE
@@ -0,0 +1,29 @@
+UglifyJS is released under the BSD license:
+
+Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above
+      copyright notice, this list of conditions and the following
+      disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials
+      provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/README.md
new file mode 100644
index 0000000..3b99441
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/README.md
@@ -0,0 +1,784 @@
+UglifyJS 2
+==========
+[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2)
+
+UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
+
+This page documents the command line utility.  For
+[API and internals documentation see my website](http://lisperator.net/uglifyjs/).
+There's also an
+[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
+Chrome and probably Safari).
+
+Install
+-------
+
+First make sure you have installed the latest version of [node.js](http://nodejs.org/)
+(You may need to restart your computer after this step).
+
+From NPM for use as a command line app:
+
+    npm install uglify-js -g
+
+From NPM for programmatic use:
+
+    npm install uglify-js
+
+From Git:
+
+    git clone git://github.com/mishoo/UglifyJS2.git
+    cd UglifyJS2
+    npm link .
+
+Usage
+-----
+
+    uglifyjs [input files] [options]
+
+UglifyJS2 can take multiple input files.  It's recommended that you pass the
+input files first, then pass the options.  UglifyJS will parse input files
+in sequence and apply any compression options.  The files are parsed in the
+same global scope, that is, a reference from a file to some
+variable/function declared in another file will be matched properly.
+
+If you want to read from STDIN instead, pass a single dash instead of input
+files.
+
+If you wish to pass your options before the input files, separate the two with
+a double dash to prevent input files being used as option arguments:
+
+    uglifyjs --compress --mangle -- input.js
+
+The available options are:
+
+```
+  --source-map                  Specify an output file where to generate source
+                                map.
+  --source-map-root             The path to the original source to be included
+                                in the source map.
+  --source-map-url              The path to the source map to be added in //#
+                                sourceMappingURL.  Defaults to the value passed
+                                with --source-map.
+  --source-map-include-sources  Pass this flag if you want to include the
+                                content of source files in the source map as
+                                sourcesContent property.
+  --in-source-map               Input source map, useful if you're compressing
+                                JS that was generated from some other original
+                                code.
+  --screw-ie8                   Pass this flag if you don't care about full
+                                compliance with Internet Explorer 6-8 quirks
+                                (by default UglifyJS will try to be IE-proof).
+  --expr                        Parse a single expression, rather than a
+                                program (for parsing JSON)
+  -p, --prefix                  Skip prefix for original filenames that appear
+                                in source maps. For example -p 3 will drop 3
+                                directories from file names and ensure they are
+                                relative paths. You can also specify -p
+                                relative, which will make UglifyJS figure out
+                                itself the relative paths between original
+                                sources, the source map and the output file.
+  -o, --output                  Output file (default STDOUT).
+  -b, --beautify                Beautify output/specify output options.
+  -m, --mangle                  Mangle names/pass mangler options.
+  -r, --reserved                Reserved names to exclude from mangling.
+  -c, --compress                Enable compressor/pass compressor options. Pass
+                                options like -c
+                                hoist_vars=false,if_return=false. Use -c with
+                                no argument to use the default compression
+                                options.
+  -d, --define                  Global definitions
+  -e, --enclose                 Embed everything in a big function, with a
+                                configurable parameter/argument list.
+  --comments                    Preserve copyright comments in the output. By
+                                default this works like Google Closure, keeping
+                                JSDoc-style comments that contain "@license" or
+                                "@preserve". You can optionally pass one of the
+                                following arguments to this flag:
+                                - "all" to keep all comments
+                                - a valid JS regexp (needs to start with a
+                                slash) to keep only comments that match.
+                                Note that currently not *all* comments can be
+                                kept when compression is on, because of dead
+                                code removal or cascading statements into
+                                sequences.
+  --preamble                    Preamble to prepend to the output.  You can use
+                                this to insert a comment, for example for
+                                licensing information.  This will not be
+                                parsed, but the source map will adjust for its
+                                presence.
+  --stats                       Display operations run time on STDERR.
+  --acorn                       Use Acorn for parsing.
+  --spidermonkey                Assume input files are SpiderMonkey AST format
+                                (as JSON).
+  --self                        Build itself (UglifyJS2) as a library (implies
+                                --wrap=UglifyJS --export-all)
+  --wrap                        Embed everything in a big function, making the
+                                “exports” and “global” variables available. You
+                                need to pass an argument to this option to
+                                specify the name that your module will take
+                                when included in, say, a browser.
+  --export-all                  Only used when --wrap, this tells UglifyJS to
+                                add code to automatically export all globals.
+  --lint                        Display some scope warnings
+  -v, --verbose                 Verbose
+  -V, --version                 Print version number and exit.
+  --noerr                       Don't throw an error for unknown options in -c,
+                                -b or -m.
+  --bare-returns                Allow return outside of functions.  Useful when
+                                minifying CommonJS modules.
+  --keep-fnames                 Do not mangle/drop function names.  Useful for
+                                code relying on Function.prototype.name.
+  --reserved-file               File containing reserved names
+  --reserve-domprops            Make (most?) DOM properties reserved for
+                                --mangle-props
+  --mangle-props                Mangle property names
+  --mangle-regex                Only mangle property names matching the regex
+  --name-cache                  File to hold mangled names mappings
+```
+
+Specify `--output` (`-o`) to declare the output file.  Otherwise the output
+goes to STDOUT.
+
+## Source map options
+
+UglifyJS2 can generate a source map file, which is highly useful for
+debugging your compressed JavaScript.  To get a source map, pass
+`--source-map output.js.map` (full path to the file where you want the
+source map dumped).
+
+Additionally you might need `--source-map-root` to pass the URL where the
+original files can be found.  In case you are passing full paths to input
+files to UglifyJS, you can use `--prefix` (`-p`) to specify the number of
+directories to drop from the path prefix when declaring files in the source
+map.
+
+For example:
+
+    uglifyjs /home/doe/work/foo/src/js/file1.js \
+             /home/doe/work/foo/src/js/file2.js \
+             -o foo.min.js \
+             --source-map foo.min.js.map \
+             --source-map-root http://foo.com/src \
+             -p 5 -c -m
+
+The above will compress and mangle `file1.js` and `file2.js`, will drop the
+output in `foo.min.js` and the source map in `foo.min.js.map`.  The source
+mapping will refer to `http://foo.com/src/js/file1.js` and
+`http://foo.com/src/js/file2.js` (in fact it will list `http://foo.com/src`
+as the source map root, and the original files as `js/file1.js` and
+`js/file2.js`).
+
+### Composed source map
+
+When you're compressing JS code that was output by a compiler such as
+CoffeeScript, mapping to the JS code won't be too helpful.  Instead, you'd
+like to map back to the original code (i.e. CoffeeScript).  UglifyJS has an
+option to take an input source map.  Assuming you have a mapping from
+CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
+compressed JS by mapping every token in the compiled JS to its original
+location.
+
+To use this feature you need to pass `--in-source-map
+/path/to/input/source.map`.  Normally the input source map should also point
+to the file containing the generated JS, so if that's correct you can omit
+input files from the command line.
+
+## Mangler options
+
+To enable the mangler you need to pass `--mangle` (`-m`).  The following
+(comma-separated) options are supported:
+
+- `sort` — to assign shorter names to most frequently used variables.  This
+  saves a few hundred bytes on jQuery before gzip, but the output is
+  _bigger_ after gzip (and seems to happen for other libraries I tried it
+  on) therefore it's not enabled by default.
+
+- `toplevel` — mangle names declared in the toplevel scope (disabled by
+  default).
+
+- `eval` — mangle names visible in scopes where `eval` or `with` are used
+  (disabled by default).
+
+When mangling is enabled but you want to prevent certain names from being
+mangled, you can declare those names with `--reserved` (`-r`) — pass a
+comma-separated list of names.  For example:
+
+    uglifyjs ... -m -r '$,require,exports'
+
+to prevent the `require`, `exports` and `$` names from being changed.
+
+### Mangling property names (`--mangle-props`)
+
+**Note:** this will probably break your code.  Mangling property names is a
+separate step, different from variable name mangling.  Pass
+`--mangle-props`.  It will mangle all properties that are seen in some
+object literal, or that are assigned to.  For example:
+
+```js
+var x = {
+  foo: 1
+};
+
+x.bar = 2;
+x["baz"] = 3;
+x[condition ? "moo" : "boo"] = 4;
+console.log(x.something());
+```
+
+In the above code, `foo`, `bar`, `baz`, `moo` and `boo` will be replaced
+with single characters, while `something()` will be left as is.
+
+In order for this to be of any use, we should avoid mangling standard JS
+names.  For instance, if your code would contain `x.length = 10`, then
+`length` becomes a candidate for mangling and it will be mangled throughout
+the code, regardless if it's being used as part of your own objects or
+accessing an array's length.  To avoid that, you can use `--reserved-file`
+to pass a filename that should contain the names to be excluded from
+mangling.  This file can be used both for excluding variable names and
+property names.  It could look like this, for example:
+
+```js
+{
+  "vars": [ "define", "require", ... ],
+  "props": [ "length", "prototype", ... ]
+}
+```
+
+`--reserved-file` can be an array of file names (either a single
+comma-separated argument, or you can pass multiple `--reserved-file`
+arguments) — in this case it will exclude names from all those files.
+
+A default exclusion file is provided in `tools/domprops.json` which should
+cover most standard JS and DOM properties defined in various browsers.  Pass
+`--reserve-domprops` to read that in.
+
+You can also use a regular expression to define which property names should be
+mangled.  For example, `--mangle-regex="/^_/"` will only mangle property names
+that start with an underscore.
+
+When you compress multiple files using this option, in order for them to
+work together in the end we need to ensure somehow that one property gets
+mangled to the same name in all of them.  For this, pass `--name-cache
+filename.json` and UglifyJS will maintain these mappings in a file which can
+then be reused.  It should be initially empty.  Example:
+
+```
+rm -f /tmp/cache.json  # start fresh
+uglifyjs file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
+uglifyjs file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js
+```
+
+Now, `part1.js` and `part2.js` will be consistent with each other in terms
+of mangled property names.
+
+Using the name cache is not necessary if you compress all your files in a
+single call to UglifyJS.
+
+## Compressor options
+
+You need to pass `--compress` (`-c`) to enable the compressor.  Optionally
+you can pass a comma-separated list of options.  Options are in the form
+`foo=bar`, or just `foo` (the latter implies a boolean option that you want
+to set `true`; it's effectively a shortcut for `foo=true`).
+
+- `sequences` -- join consecutive simple statements using the comma operator
+
+- `properties` -- rewrite property access using the dot notation, for
+  example `foo["bar"] → foo.bar`
+
+- `dead_code` -- remove unreachable code
+
+- `drop_debugger` -- remove `debugger;` statements
+
+- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below)
+
+- `conditionals` -- apply optimizations for `if`-s and conditional
+  expressions
+
+- `comparisons` -- apply certain optimizations to binary nodes, for example:
+  `!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes,
+  e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
+
+- `evaluate` -- attempt to evaluate constant expressions
+
+- `booleans` -- various optimizations for boolean context, for example `!!a
+  ? b : c → a ? b : c`
+
+- `loops` -- optimizations for `do`, `while` and `for` loops when we can
+  statically determine the condition
+
+- `unused` -- drop unreferenced functions and variables
+
+- `hoist_funs` -- hoist function declarations
+
+- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
+  by default because it seems to increase the size of the output in general)
+
+- `if_return` -- optimizations for if/return and if/continue
+
+- `join_vars` -- join consecutive `var` statements
+
+- `cascade` -- small optimization for sequences, transform `x, x` into `x`
+  and `x = something(), x` into `x = something()`
+
+- `warnings` -- display warnings when dropping unreachable code or unused
+  declarations etc.
+
+- `negate_iife` -- negate "Immediately-Called Function Expressions"
+  where the return value is discarded, to avoid the parens that the
+  code generator would insert.
+
+- `pure_getters` -- the default is `false`.  If you pass `true` for
+  this, UglifyJS will assume that object property access
+  (e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects.
+
+- `pure_funcs` -- default `null`.  You can pass an array of names and
+  UglifyJS will assume that those functions do not produce side
+  effects.  DANGER: will not check if the name is redefined in scope.
+  An example case here, for instance `var q = Math.floor(a/b)`.  If
+  variable `q` is not used elsewhere, UglifyJS will drop it, but will
+  still keep the `Math.floor(a/b)`, not knowing what it does.  You can
+  pass `pure_funcs: [ 'Math.floor' ]` to let it know that this
+  function won't produce any side effect, in which case the whole
+  statement would get discarded.  The current implementation adds some
+  overhead (compression will be slower).
+
+- `drop_console` -- default `false`.  Pass `true` to discard calls to
+  `console.*` functions.
+
+- `keep_fargs` -- default `false`.  Pass `true` to prevent the
+  compressor from discarding unused function arguments.  You need this
+  for code which relies on `Function.length`.
+
+### The `unsafe` option
+
+It enables some transformations that *might* break code logic in certain
+contrived cases, but should be fine for most code.  You might want to try it
+on your own code, it should reduce the minified size.  Here's what happens
+when this flag is on:
+
+- `new Array(1, 2, 3)` or `Array(1, 2, 3)` → `[ 1, 2, 3 ]`
+- `new Object()` → `{}`
+- `String(exp)` or `exp.toString()` → `"" + exp`
+- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
+- `typeof foo == "undefined"` → `foo === void 0`
+- `void 0` → `undefined` (if there is a variable named "undefined" in
+  scope; we do it because the variable name will be mangled, typically
+  reduced to a single character)
+- discards unused function arguments (affects `function.length`)
+
+### Conditional compilation
+
+You can use the `--define` (`-d`) switch in order to declare global
+variables that UglifyJS will assume to be constants (unless defined in
+scope).  For example if you pass `--define DEBUG=false` then, coupled with
+dead code removal UglifyJS will discard the following from the output:
+```javascript
+if (DEBUG) {
+	console.log("debug stuff");
+}
+```
+
+UglifyJS will warn about the condition being always false and about dropping
+unreachable code; for now there is no option to turn off only this specific
+warning, you can pass `warnings=false` to turn off *all* warnings.
+
+Another way of doing that is to declare your globals as constants in a
+separate file and include it into the build.  For example you can have a
+`build/defines.js` file with the following:
+```javascript
+const DEBUG = false;
+const PRODUCTION = true;
+// etc.
+```
+
+and build your code like this:
+
+    uglifyjs build/defines.js js/foo.js js/bar.js... -c
+
+UglifyJS will notice the constants and, since they cannot be altered, it
+will evaluate references to them to the value itself and drop unreachable
+code as usual.  The possible downside of this approach is that the build
+will contain the `const` declarations.
+
+<a name="codegen-options"></a>
+## Beautifier options
+
+The code generator tries to output shortest code possible by default.  In
+case you want beautified output, pass `--beautify` (`-b`).  Optionally you
+can pass additional arguments that control the code output:
+
+- `beautify` (default `true`) -- whether to actually beautify the output.
+  Passing `-b` will set this to true, but you might need to pass `-b` even
+  when you want to generate minified code, in order to specify additional
+  arguments, so you can use `-b beautify=false` to override it.
+- `indent-level` (default 4)
+- `indent-start` (default 0) -- prefix all lines by that many spaces
+- `quote-keys` (default `false`) -- pass `true` to quote all keys in literal
+  objects
+- `space-colon` (default `true`) -- insert a space after the colon signs
+- `ascii-only` (default `false`) -- escape Unicode characters in strings and
+  regexps
+- `inline-script` (default `false`) -- escape the slash in occurrences of
+  `</script` in strings
+- `width` (default 80) -- only takes effect when beautification is on, this
+  specifies an (orientative) line width that the beautifier will try to
+  obey.  It refers to the width of the line text (excluding indentation).
+  It doesn't work very well currently, but it does make the code generated
+  by UglifyJS more readable.
+- `max-line-len` (default 32000) -- maximum line length (for uglified code)
+- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
+  `do`, `while` or `with` statements, even if their body is a single
+  statement.
+- `semicolons` (default `true`) -- separate statements with semicolons.  If
+  you pass `false` then whenever possible we will use a newline instead of a
+  semicolon, leading to more readable output of uglified code (size before
+  gzip could be smaller; size after gzip insignificantly larger).
+- `preamble` (default `null`) -- when passed it must be a string and
+  it will be prepended to the output literally.  The source map will
+  adjust for this text.  Can be used to insert a comment containing
+  licensing information, for example.
+- `quote_style` (default `0`) -- preferred quote style for strings (affects
+  quoted property names and directives as well):
+  - `0` -- prefers double quotes, switches to single quotes when there are
+    more double quotes in the string itself.
+  - `1` -- always use single quotes
+  - `2` -- always use double quotes
+  - `3` -- always use the original quotes
+
+### Keeping copyright notices or other comments
+
+You can pass `--comments` to retain certain comments in the output.  By
+default it will keep JSDoc-style comments that contain "@preserve",
+"@license" or "@cc_on" (conditional compilation for IE).  You can pass
+`--comments all` to keep all the comments, or a valid JavaScript regexp to
+keep only comments that match this regexp.  For example `--comments
+'/foo|bar/'` will keep only comments that contain "foo" or "bar".
+
+Note, however, that there might be situations where comments are lost.  For
+example:
+```javascript
+function f() {
+	/** @preserve Foo Bar */
+	function g() {
+	  // this function is never called
+	}
+	return something();
+}
+```
+
+Even though it has "@preserve", the comment will be lost because the inner
+function `g` (which is the AST node to which the comment is attached to) is
+discarded by the compressor as not referenced.
+
+The safest comments where to place copyright information (or other info that
+needs to be kept in the output) are comments attached to toplevel nodes.
+
+## Support for the SpiderMonkey AST
+
+UglifyJS2 has its own abstract syntax tree format; for
+[practical reasons](http://lisperator.net/blog/uglifyjs-why-not-switching-to-spidermonkey-ast/)
+we can't easily change to using the SpiderMonkey AST internally.  However,
+UglifyJS now has a converter which can import a SpiderMonkey AST.
+
+For example [Acorn][acorn] is a super-fast parser that produces a
+SpiderMonkey AST.  It has a small CLI utility that parses one file and dumps
+the AST in JSON on the standard output.  To use UglifyJS to mangle and
+compress that:
+
+    acorn file.js | uglifyjs --spidermonkey -m -c
+
+The `--spidermonkey` option tells UglifyJS that all input files are not
+JavaScript, but JS code described in SpiderMonkey AST in JSON.  Therefore we
+don't use our own parser in this case, but just transform that AST into our
+internal AST.
+
+### Use Acorn for parsing
+
+More for fun, I added the `--acorn` option which will use Acorn to do all
+the parsing.  If you pass this option, UglifyJS will `require("acorn")`.
+
+Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
+converting the SpiderMonkey tree that Acorn produces takes another 150ms so
+in total it's a bit more than just using UglifyJS's own parser.
+
+### Using UglifyJS to transform SpiderMonkey AST
+
+Now you can use UglifyJS as any other intermediate tool for transforming
+JavaScript ASTs in SpiderMonkey format.
+
+Example:
+
+```javascript
+function uglify(ast, options, mangle) {
+  // Conversion from SpiderMonkey AST to internal format
+  var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
+
+  // Compression
+  uAST.figure_out_scope();
+  uAST = uAST.transform(UglifyJS.Compressor(options));
+
+  // Mangling (optional)
+  if (mangle) {
+    uAST.figure_out_scope();
+    uAST.compute_char_frequency();
+    uAST.mangle_names();
+  }
+
+  // Back-conversion to SpiderMonkey AST
+  return uAST.to_mozilla_ast();
+}
+```
+
+Check out
+[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
+for details.
+
+API Reference
+-------------
+
+Assuming installation via NPM, you can load UglifyJS in your application
+like this:
+```javascript
+var UglifyJS = require("uglify-js");
+```
+
+It exports a lot of names, but I'll discuss here the basics that are needed
+for parsing, mangling and compressing a piece of code.  The sequence is (1)
+parse, (2) compress, (3) mangle, (4) generate output code.
+
+### The simple way
+
+There's a single toplevel function which combines all the steps.  If you
+don't need additional customization, you might want to go with `minify`.
+Example:
+```javascript
+var result = UglifyJS.minify("/path/to/file.js");
+console.log(result.code); // minified output
+// if you need to pass code instead of file name
+var result = UglifyJS.minify("var b = function () {};", {fromString: true});
+```
+
+You can also compress multiple files:
+```javascript
+var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
+console.log(result.code);
+```
+
+To generate a source map:
+```javascript
+var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
+	outSourceMap: "out.js.map"
+});
+console.log(result.code); // minified output
+console.log(result.map);
+```
+
+Note that the source map is not saved in a file, it's just returned in
+`result.map`.  The value passed for `outSourceMap` is only used to set the
+`file` attribute in the source map (see [the spec][sm-spec]).
+
+You can also specify sourceRoot property to be included in source map:
+```javascript
+var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
+	outSourceMap: "out.js.map",
+	sourceRoot: "http://example.com/src"
+});
+```
+
+If you're compressing compiled JavaScript and have a source map for it, you
+can use the `inSourceMap` argument:
+```javascript
+var result = UglifyJS.minify("compiled.js", {
+	inSourceMap: "compiled.js.map",
+	outSourceMap: "minified.js.map"
+});
+// same as before, it returns `code` and `map`
+```
+
+If your input source map is not in a file, you can pass it in as an object
+using the `inSourceMap` argument:
+
+```javascript
+var result = UglifyJS.minify("compiled.js", {
+	inSourceMap: JSON.parse(my_source_map_string),
+	outSourceMap: "minified.js.map"
+});
+```
+
+The `inSourceMap` is only used if you also request `outSourceMap` (it makes
+no sense otherwise).
+
+Other options:
+
+- `warnings` (default `false`) — pass `true` to display compressor warnings.
+
+- `fromString` (default `false`) — if you pass `true` then you can pass
+  JavaScript source code, rather than file names.
+
+- `mangle` — pass `false` to skip mangling names.
+
+- `output` (default `null`) — pass an object if you wish to specify
+  additional [output options][codegen].  The defaults are optimized
+  for best compression.
+
+- `compress` (default `{}`) — pass `false` to skip compressing entirely.
+  Pass an object to specify custom [compressor options][compressor].
+
+We could add more options to `UglifyJS.minify` — if you need additional
+functionality please suggest!
+
+### The hard way
+
+Following there's more detailed API info, in case the `minify` function is
+too simple for your needs.
+
+#### The parser
+```javascript
+var toplevel_ast = UglifyJS.parse(code, options);
+```
+
+`options` is optional and if present it must be an object.  The following
+properties are available:
+
+- `strict` — disable automatic semicolon insertion and support for trailing
+  comma in arrays and objects
+- `filename` — the name of the file where this code is coming from
+- `toplevel` — a `toplevel` node (as returned by a previous invocation of
+  `parse`)
+
+The last two options are useful when you'd like to minify multiple files and
+get a single file as the output and a proper source map.  Our CLI tool does
+something like this:
+```javascript
+var toplevel = null;
+files.forEach(function(file){
+	var code = fs.readFileSync(file, "utf8");
+	toplevel = UglifyJS.parse(code, {
+		filename: file,
+		toplevel: toplevel
+	});
+});
+```
+
+After this, we have in `toplevel` a big AST containing all our files, with
+each token having proper information about where it came from.
+
+#### Scope information
+
+UglifyJS contains a scope analyzer that you need to call manually before
+compressing or mangling.  Basically it augments various nodes in the AST
+with information about where is a name defined, how many times is a name
+referenced, if it is a global or not, if a function is using `eval` or the
+`with` statement etc.  I will discuss this some place else, for now what's
+important to know is that you need to call the following before doing
+anything with the tree:
+```javascript
+toplevel.figure_out_scope()
+```
+
+#### Compression
+
+Like this:
+```javascript
+var compressor = UglifyJS.Compressor(options);
+var compressed_ast = toplevel.transform(compressor);
+```
+
+The `options` can be missing.  Available options are discussed above in
+“Compressor options”.  Defaults should lead to best compression in most
+scripts.
+
+The compressor is destructive, so don't rely that `toplevel` remains the
+original tree.
+
+#### Mangling
+
+After compression it is a good idea to call again `figure_out_scope` (since
+the compressor might drop unused variables / unreachable code and this might
+change the number of identifiers or their position).  Optionally, you can
+call a trick that helps after Gzip (counting character frequency in
+non-mangleable words).  Example:
+```javascript
+compressed_ast.figure_out_scope();
+compressed_ast.compute_char_frequency();
+compressed_ast.mangle_names();
+```
+
+#### Generating output
+
+AST nodes have a `print` method that takes an output stream.  Essentially,
+to generate code you do this:
+```javascript
+var stream = UglifyJS.OutputStream(options);
+compressed_ast.print(stream);
+var code = stream.toString(); // this is your minified code
+```
+
+or, for a shortcut you can do:
+```javascript
+var code = compressed_ast.print_to_string(options);
+```
+
+As usual, `options` is optional.  The output stream accepts a lot of options,
+most of them documented above in section “Beautifier options”.  The two
+which we care about here are `source_map` and `comments`.
+
+#### Keeping comments in the output
+
+In order to keep certain comments in the output you need to pass the
+`comments` option.  Pass a RegExp or a function.  If you pass a RegExp, only
+those comments whose body matches the regexp will be kept.  Note that body
+means without the initial `//` or `/*`.  If you pass a function, it will be
+called for every comment in the tree and will receive two arguments: the
+node that the comment is attached to, and the comment token itself.
+
+The comment token has these properties:
+
+- `type`: "comment1" for single-line comments or "comment2" for multi-line
+  comments
+- `value`: the comment body
+- `pos` and `endpos`: the start/end positions (zero-based indexes) in the
+  original code where this comment appears
+- `line` and `col`: the line and column where this comment appears in the
+  original code
+- `file` — the file name of the original file
+- `nlb` — true if there was a newline before this comment in the original
+  code, or if this comment contains a newline.
+
+Your function should return `true` to keep the comment, or a falsy value
+otherwise.
+
+#### Generating a source mapping
+
+You need to pass the `source_map` argument when calling `print`.  It needs
+to be a `SourceMap` object (which is a thin wrapper on top of the
+[source-map][source-map] library).
+
+Example:
+```javascript
+var source_map = UglifyJS.SourceMap(source_map_options);
+var stream = UglifyJS.OutputStream({
+	...
+	source_map: source_map
+});
+compressed_ast.print(stream);
+
+var code = stream.toString();
+var map = source_map.toString(); // json output for your source map
+```
+
+The `source_map_options` (optional) can contain the following properties:
+
+- `file`: the name of the JavaScript output file that this mapping refers to
+- `root`: the `sourceRoot` property (see the [spec][sm-spec])
+- `orig`: the "original source map", handy when you compress generated JS
+  and want to map the minified output back to the original code where it
+  came from.  It can be simply a string in JSON, or a JSON object containing
+  the original source map.
+
+  [acorn]: https://github.com/marijnh/acorn
+  [source-map]: https://github.com/mozilla/source-map
+  [sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+  [codegen]: http://lisperator.net/uglifyjs/codegen
+  [compressor]: http://lisperator.net/uglifyjs/compress
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/bin/extract-props.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/bin/extract-props.js
new file mode 100644
index 0000000..a5b6145
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/bin/extract-props.js
@@ -0,0 +1,77 @@
+#! /usr/bin/env node
+
+"use strict";
+
+var U2 = require("../tools/node");
+var fs = require("fs");
+var yargs = require("yargs");
+var ARGS = yargs
+    .describe("o", "Output file")
+    .argv;
+var files = ARGS._.slice();
+var output = {
+    vars: {},
+    props: {}
+};
+
+if (ARGS.o) try {
+    output = JSON.parse(fs.readFileSync(ARGS.o, "utf8"));
+} catch(ex) {}
+
+files.forEach(getProps);
+
+if (ARGS.o) {
+    fs.writeFileSync(ARGS.o, JSON.stringify(output, null, 2), "utf8");
+} else {
+    console.log("%s", JSON.stringify(output, null, 2));
+}
+
+function getProps(filename) {
+    var code = fs.readFileSync(filename, "utf8");
+    var ast = U2.parse(code);
+
+    ast.walk(new U2.TreeWalker(function(node){
+        if (node instanceof U2.AST_ObjectKeyVal) {
+            add(node.key);
+        }
+        else if (node instanceof U2.AST_ObjectProperty) {
+            add(node.key.name);
+        }
+        else if (node instanceof U2.AST_Dot) {
+            add(node.property);
+        }
+        else if (node instanceof U2.AST_Sub) {
+            addStrings(node.property);
+        }
+    }));
+
+    function addStrings(node) {
+        var out = {};
+        try {
+            (function walk(node){
+                node.walk(new U2.TreeWalker(function(node){
+                    if (node instanceof U2.AST_Seq) {
+                        walk(node.cdr);
+                        return true;
+                    }
+                    if (node instanceof U2.AST_String) {
+                        add(node.value);
+                        return true;
+                    }
+                    if (node instanceof U2.AST_Conditional) {
+                        walk(node.consequent);
+                        walk(node.alternative);
+                        return true;
+                    }
+                    throw out;
+                }));
+            })(node);
+        } catch(ex) {
+            if (ex !== out) throw ex;
+        }
+    }
+
+    function add(name) {
+        output.props[name] = true;
+    }
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/bin/uglifyjs b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/bin/uglifyjs
new file mode 100644
index 0000000..4768f76
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/bin/uglifyjs
@@ -0,0 +1,560 @@
+#! /usr/bin/env node
+// -*- js -*-
+
+"use strict";
+
+var UglifyJS = require("../tools/node");
+var sys = require("util");
+var yargs = require("yargs");
+var fs = require("fs");
+var path = require("path");
+var async = require("async");
+var acorn;
+var ARGS = yargs
+    .usage("$0 input1.js [input2.js ...] [options]\n\
+Use a single dash to read input from the standard input.\
+\n\n\
+NOTE: by default there is no mangling/compression.\n\
+Without [options] it will simply parse input files and dump the AST\n\
+with whitespace and comments discarded.  To achieve compression and\n\
+mangling you need to use `-c` and `-m`.\
+")
+    .describe("source-map", "Specify an output file where to generate source map.")
+    .describe("source-map-root", "The path to the original source to be included in the source map.")
+    .describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL.  Defaults to the value passed with --source-map.")
+    .describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
+    .describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
+    .describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
+    .describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
+    .describe("p", "Skip prefix for original filenames that appear in source maps. \
+For example -p 3 will drop 3 directories from file names and ensure they are relative paths. \
+You can also specify -p relative, which will make UglifyJS figure out itself the relative paths between original sources, \
+the source map and the output file.")
+    .describe("o", "Output file (default STDOUT).")
+    .describe("b", "Beautify output/specify output options.")
+    .describe("m", "Mangle names/pass mangler options.")
+    .describe("r", "Reserved names to exclude from mangling.")
+    .describe("c", "Enable compressor/pass compressor options. \
+Pass options like -c hoist_vars=false,if_return=false. \
+Use -c with no argument to use the default compression options.")
+    .describe("d", "Global definitions")
+    .describe("e", "Embed everything in a big function, with a configurable parameter/argument list.")
+
+    .describe("comments", "Preserve copyright comments in the output. \
+By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
+You can optionally pass one of the following arguments to this flag:\n\
+- \"all\" to keep all comments\n\
+- a valid JS regexp (needs to start with a slash) to keep only comments that match.\n\
+\
+Note that currently not *all* comments can be kept when compression is on, \
+because of dead code removal or cascading statements into sequences.")
+
+    .describe("preamble", "Preamble to prepend to the output.  You can use this to insert a \
+comment, for example for licensing information.  This will not be \
+parsed, but the source map will adjust for its presence.")
+
+    .describe("stats", "Display operations run time on STDERR.")
+    .describe("acorn", "Use Acorn for parsing.")
+    .describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
+    .describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
+    .describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
+You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
+    .describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
+    .describe("lint", "Display some scope warnings")
+    .describe("v", "Verbose")
+    .describe("V", "Print version number and exit.")
+    .describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
+    .describe("bare-returns", "Allow return outside of functions.  Useful when minifying CommonJS modules.")
+    .describe("keep-fnames", "Do not mangle/drop function names.  Useful for code relying on Function.prototype.name.")
+    .describe("quotes", "Quote style (0 - auto, 1 - single, 2 - double, 3 - original)")
+    .describe("reserved-file", "File containing reserved names")
+    .describe("reserve-domprops", "Make (most?) DOM properties reserved for --mangle-props")
+    .describe("mangle-props", "Mangle property names")
+    .describe("mangle-regex", "Only mangle property names matching the regex")
+    .describe("name-cache", "File to hold mangled names mappings")
+
+    .alias("p", "prefix")
+    .alias("o", "output")
+    .alias("v", "verbose")
+    .alias("b", "beautify")
+    .alias("m", "mangle")
+    .alias("c", "compress")
+    .alias("d", "define")
+    .alias("r", "reserved")
+    .alias("V", "version")
+    .alias("e", "enclose")
+    .alias("q", "quotes")
+
+    .string("source-map")
+    .string("source-map-root")
+    .string("source-map-url")
+    .string("b")
+    .string("beautify")
+    .string("m")
+    .string("mangle")
+    .string("c")
+    .string("compress")
+    .string("d")
+    .string("define")
+    .string("e")
+    .string("enclose")
+    .string("comments")
+    .string("wrap")
+    .string("p")
+    .string("prefix")
+    .string("name-cache")
+    .array("reserved-file")
+
+    .boolean("expr")
+    .boolean("source-map-include-sources")
+    .boolean("screw-ie8")
+    .boolean("export-all")
+    .boolean("self")
+    .boolean("v")
+    .boolean("verbose")
+    .boolean("stats")
+    .boolean("acorn")
+    .boolean("spidermonkey")
+    .boolean("lint")
+    .boolean("V")
+    .boolean("version")
+    .boolean("noerr")
+    .boolean("bare-returns")
+    .boolean("keep-fnames")
+    .boolean("mangle-props")
+    .boolean("reserve-domprops")
+
+    .wrap(80)
+
+    .argv
+;
+
+normalize(ARGS);
+
+if (ARGS.noerr) {
+    UglifyJS.DefaultsError.croak = function(msg, defs) {
+        print_error("WARN: " + msg);
+    };
+}
+
+if (ARGS.version || ARGS.V) {
+    var json = require("../package.json");
+    print(json.name + ' ' + json.version);
+    process.exit(0);
+}
+
+if (ARGS.ast_help) {
+    var desc = UglifyJS.describe_ast();
+    print(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
+    process.exit(0);
+}
+
+if (ARGS.h || ARGS.help) {
+    print(yargs.help());
+    process.exit(0);
+}
+
+if (ARGS.acorn) {
+    acorn = require("acorn");
+}
+
+var COMPRESS = getOptions("c", true);
+var MANGLE = getOptions("m", true);
+var BEAUTIFY = getOptions("b", true);
+var RESERVED = null;
+
+if (ARGS.reserved_file) ARGS.reserved_file.forEach(function(filename){
+    RESERVED = UglifyJS.readReservedFile(filename, RESERVED);
+});
+
+if (ARGS.reserve_domprops) {
+    RESERVED = UglifyJS.readDefaultReservedFile(RESERVED);
+}
+
+if (ARGS.d) {
+    if (COMPRESS) COMPRESS.global_defs = getOptions("d");
+}
+
+if (ARGS.r) {
+    if (MANGLE) MANGLE.except = ARGS.r.replace(/^\s+|\s+$/g).split(/\s*,+\s*/);
+}
+
+if (RESERVED && MANGLE) {
+    if (!MANGLE.except) MANGLE.except = RESERVED.vars;
+    else MANGLE.except = MANGLE.except.concat(RESERVED.vars);
+}
+
+function readNameCache(key) {
+    return UglifyJS.readNameCache(ARGS.name_cache, key);
+}
+
+function writeNameCache(key, cache) {
+    return UglifyJS.writeNameCache(ARGS.name_cache, key, cache);
+}
+
+function extractRegex(str) {
+  if (/^\/.*\/[a-zA-Z]*$/.test(str)) {
+    var regex_pos = str.lastIndexOf("/");
+    return new RegExp(str.substr(1, regex_pos - 1), str.substr(regex_pos + 1));
+  } else {
+    throw new Error("Invalid regular expression: " + str);
+  }
+}
+
+if (ARGS.quotes === true) {
+    ARGS.quotes = 3;
+}
+
+var OUTPUT_OPTIONS = {
+    beautify    : BEAUTIFY ? true : false,
+    preamble    : ARGS.preamble || null,
+    quote_style : ARGS.quotes != null ? ARGS.quotes : 0
+};
+
+if (ARGS.screw_ie8) {
+    if (COMPRESS) COMPRESS.screw_ie8 = true;
+    if (MANGLE) MANGLE.screw_ie8 = true;
+    OUTPUT_OPTIONS.screw_ie8 = true;
+}
+
+if (ARGS.keep_fnames) {
+    if (COMPRESS) COMPRESS.keep_fnames = true;
+    if (MANGLE) MANGLE.keep_fnames = true;
+}
+
+if (BEAUTIFY)
+    UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
+
+if (ARGS.comments != null) {
+    if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) {
+        try {
+            OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
+        } catch (e) {
+            print_error("ERROR: Invalid --comments: " + e.message);
+        }
+    } else if (ARGS.comments == "all") {
+        OUTPUT_OPTIONS.comments = true;
+    } else {
+        OUTPUT_OPTIONS.comments = function(node, comment) {
+            var text = comment.value;
+            var type = comment.type;
+            if (type == "comment2") {
+                // multiline comment
+                return /@preserve|@license|@cc_on/i.test(text);
+            }
+        }
+    }
+}
+
+var files = ARGS._.slice();
+
+if (ARGS.self) {
+    if (files.length > 0) {
+        print_error("WARN: Ignoring input files since --self was passed");
+    }
+    files = UglifyJS.FILES;
+    if (!ARGS.wrap) ARGS.wrap = "UglifyJS";
+    ARGS.export_all = true;
+}
+
+var ORIG_MAP = ARGS.in_source_map;
+
+if (ORIG_MAP) {
+    ORIG_MAP = JSON.parse(fs.readFileSync(ORIG_MAP));
+    if (files.length == 0) {
+        print_error("INFO: Using file from the input source map: " + ORIG_MAP.file);
+        files = [ ORIG_MAP.file ];
+    }
+    if (ARGS.source_map_root == null) {
+        ARGS.source_map_root = ORIG_MAP.sourceRoot;
+    }
+}
+
+if (files.length == 0) {
+    files = [ "-" ];
+}
+
+if (files.indexOf("-") >= 0 && ARGS.source_map) {
+    print_error("ERROR: Source map doesn't work with input from STDIN");
+    process.exit(1);
+}
+
+if (files.filter(function(el){ return el == "-" }).length > 1) {
+    print_error("ERROR: Can read a single file from STDIN (two or more dashes specified)");
+    process.exit(1);
+}
+
+var STATS = {};
+var OUTPUT_FILE = ARGS.o;
+var TOPLEVEL = null;
+var P_RELATIVE = ARGS.p && ARGS.p == "relative";
+var SOURCES_CONTENT = {};
+
+var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
+    file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
+    root: ARGS.source_map_root,
+    orig: ORIG_MAP,
+}) : null;
+
+OUTPUT_OPTIONS.source_map = SOURCE_MAP;
+
+try {
+    var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
+    var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
+} catch(ex) {
+    if (ex instanceof UglifyJS.DefaultsError) {
+        print_error(ex.msg);
+        print_error("Supported options:");
+        print_error(sys.inspect(ex.defs));
+        process.exit(1);
+    }
+}
+
+async.eachLimit(files, 1, function (file, cb) {
+    read_whole_file(file, function (err, code) {
+        if (err) {
+            print_error("ERROR: can't read file: " + file);
+            process.exit(1);
+        }
+        if (ARGS.p != null) {
+            if (P_RELATIVE) {
+                file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/');
+            } else {
+                var p = parseInt(ARGS.p, 10);
+                if (!isNaN(p)) {
+                    file = file.replace(/^\/+/, "").split(/\/+/).slice(ARGS.p).join("/");
+                }
+            }
+        }
+        SOURCES_CONTENT[file] = code;
+        time_it("parse", function(){
+            if (ARGS.spidermonkey) {
+                var program = JSON.parse(code);
+                if (!TOPLEVEL) TOPLEVEL = program;
+                else TOPLEVEL.body = TOPLEVEL.body.concat(program.body);
+            }
+            else if (ARGS.acorn) {
+                TOPLEVEL = acorn.parse(code, {
+                    locations     : true,
+                    sourceFile    : file,
+                    program       : TOPLEVEL
+                });
+            }
+            else {
+                try {
+                    TOPLEVEL = UglifyJS.parse(code, {
+                        filename     : file,
+                        toplevel     : TOPLEVEL,
+                        expression   : ARGS.expr,
+                        bare_returns : ARGS.bare_returns,
+                    });
+                } catch(ex) {
+                    if (ex instanceof UglifyJS.JS_Parse_Error) {
+                        print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
+                        print_error(ex.message);
+                        print_error(ex.stack);
+                        process.exit(1);
+                    }
+                    throw ex;
+                }
+            };
+        });
+        cb();
+    });
+}, function () {
+    if (ARGS.acorn || ARGS.spidermonkey) time_it("convert_ast", function(){
+        TOPLEVEL = UglifyJS.AST_Node.from_mozilla_ast(TOPLEVEL);
+    });
+
+    if (ARGS.wrap != null) {
+        TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all);
+    }
+
+    if (ARGS.enclose != null) {
+        var arg_parameter_list = ARGS.enclose;
+        if (arg_parameter_list === true) {
+            arg_parameter_list = [];
+        }
+        else if (!(arg_parameter_list instanceof Array)) {
+            arg_parameter_list = [arg_parameter_list];
+        }
+        TOPLEVEL = TOPLEVEL.wrap_enclose(arg_parameter_list);
+    }
+
+    if (ARGS.mangle_props || ARGS.name_cache) (function(){
+        var reserved = RESERVED ? RESERVED.props : null;
+        var cache = readNameCache("props");
+        var regex;
+
+        try {
+          regex = ARGS.mangle_regex ? extractRegex(ARGS.mangle_regex) : null;
+        } catch (e) {
+            print_error("ERROR: Invalid --mangle-regex: " + e.message);
+            process.exit(1);
+        }
+
+        TOPLEVEL = UglifyJS.mangle_properties(TOPLEVEL, {
+            reserved   : reserved,
+            cache      : cache,
+            only_cache : !ARGS.mangle_props,
+            regex      : regex
+        });
+        writeNameCache("props", cache);
+    })();
+
+    var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint;
+    var TL_CACHE = readNameCache("vars");
+
+    if (SCOPE_IS_NEEDED) {
+        time_it("scope", function(){
+            TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
+            if (ARGS.lint) {
+                TOPLEVEL.scope_warnings();
+            }
+        });
+    }
+
+    if (COMPRESS) {
+        time_it("squeeze", function(){
+            TOPLEVEL = TOPLEVEL.transform(compressor);
+        });
+    }
+
+    if (SCOPE_IS_NEEDED) {
+        time_it("scope", function(){
+            TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
+            if (MANGLE && !TL_CACHE) {
+                TOPLEVEL.compute_char_frequency(MANGLE);
+            }
+        });
+    }
+
+    if (MANGLE) time_it("mangle", function(){
+        MANGLE.cache = TL_CACHE;
+        TOPLEVEL.mangle_names(MANGLE);
+    });
+
+    writeNameCache("vars", TL_CACHE);
+
+    if (ARGS.source_map_include_sources) {
+        for (var file in SOURCES_CONTENT) {
+            if (SOURCES_CONTENT.hasOwnProperty(file)) {
+                SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
+            }
+        }
+    }
+
+    time_it("generate", function(){
+        TOPLEVEL.print(output);
+    });
+
+    output = output.get();
+
+    if (SOURCE_MAP) {
+        fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
+        var source_map_url = ARGS.source_map_url || (
+            P_RELATIVE
+                ? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
+                : ARGS.source_map
+        );
+        output += "\n//# sourceMappingURL=" + source_map_url;
+    }
+
+    if (OUTPUT_FILE) {
+        fs.writeFileSync(OUTPUT_FILE, output, "utf8");
+    } else {
+        print(output);
+    }
+
+    if (ARGS.stats) {
+        print_error(UglifyJS.string_template("Timing information (compressed {count} files):", {
+            count: files.length
+        }));
+        for (var i in STATS) if (STATS.hasOwnProperty(i)) {
+            print_error(UglifyJS.string_template("- {name}: {time}s", {
+                name: i,
+                time: (STATS[i] / 1000).toFixed(3)
+            }));
+        }
+    }
+});
+
+/* -----[ functions ]----- */
+
+function normalize(o) {
+    for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
+        o[i.replace(/-/g, "_")] = o[i];
+        delete o[i];
+    }
+}
+
+function getOptions(x, constants) {
+    x = ARGS[x];
+    if (x == null) return null;
+    var ret = {};
+    if (x !== "") {
+        var ast;
+        try {
+            ast = UglifyJS.parse(x, { expression: true });
+        } catch(ex) {
+            if (ex instanceof UglifyJS.JS_Parse_Error) {
+                print_error("Error parsing arguments in: " + x);
+                process.exit(1);
+            }
+        }
+        ast.walk(new UglifyJS.TreeWalker(function(node){
+            if (node instanceof UglifyJS.AST_Seq) return; // descend
+            if (node instanceof UglifyJS.AST_Assign) {
+                var name = node.left.print_to_string({ beautify: false }).replace(/-/g, "_");
+                var value = node.right;
+                if (constants)
+                    value = new Function("return (" + value.print_to_string() + ")")();
+                ret[name] = value;
+                return true;    // no descend
+            }
+            if (node instanceof UglifyJS.AST_Symbol || node instanceof UglifyJS.AST_Binary) {
+                var name = node.print_to_string({ beautify: false }).replace(/-/g, "_");
+                ret[name] = true;
+                return true;    // no descend
+            }
+            print_error(node.TYPE)
+            print_error("Error parsing arguments in: " + x);
+            process.exit(1);
+        }));
+    }
+    return ret;
+}
+
+function read_whole_file(filename, cb) {
+    if (filename == "-") {
+        var chunks = [];
+        process.stdin.setEncoding('utf-8');
+        process.stdin.on('data', function (chunk) {
+            chunks.push(chunk);
+        }).on('end', function () {
+            cb(null, chunks.join(""));
+        });
+        process.openStdin();
+    } else {
+        fs.readFile(filename, "utf-8", cb);
+    }
+}
+
+function time_it(name, cont) {
+    var t1 = new Date().getTime();
+    var ret = cont();
+    if (ARGS.stats) {
+        var spent = new Date().getTime() - t1;
+        if (STATS[name]) STATS[name] += spent;
+        else STATS[name] = spent;
+    }
+    return ret;
+}
+
+function print_error(msg) {
+    console.error("%s", msg);
+}
+
+function print(txt) {
+    console.log("%s", txt);
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/ast.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/ast.js
new file mode 100644
index 0000000..2e539cf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/ast.js
@@ -0,0 +1,995 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+function DEFNODE(type, props, methods, base) {
+    if (arguments.length < 4) base = AST_Node;
+    if (!props) props = [];
+    else props = props.split(/\s+/);
+    var self_props = props;
+    if (base && base.PROPS)
+        props = props.concat(base.PROPS);
+    var code = "return function AST_" + type + "(props){ if (props) { ";
+    for (var i = props.length; --i >= 0;) {
+        code += "this." + props[i] + " = props." + props[i] + ";";
+    }
+    var proto = base && new base;
+    if (proto && proto.initialize || (methods && methods.initialize))
+        code += "this.initialize();";
+    code += "}}";
+    var ctor = new Function(code)();
+    if (proto) {
+        ctor.prototype = proto;
+        ctor.BASE = base;
+    }
+    if (base) base.SUBCLASSES.push(ctor);
+    ctor.prototype.CTOR = ctor;
+    ctor.PROPS = props || null;
+    ctor.SELF_PROPS = self_props;
+    ctor.SUBCLASSES = [];
+    if (type) {
+        ctor.prototype.TYPE = ctor.TYPE = type;
+    }
+    if (methods) for (i in methods) if (methods.hasOwnProperty(i)) {
+        if (/^\$/.test(i)) {
+            ctor[i.substr(1)] = methods[i];
+        } else {
+            ctor.prototype[i] = methods[i];
+        }
+    }
+    ctor.DEFMETHOD = function(name, method) {
+        this.prototype[name] = method;
+    };
+    return ctor;
+};
+
+var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", {
+}, null);
+
+var AST_Node = DEFNODE("Node", "start end", {
+    clone: function() {
+        return new this.CTOR(this);
+    },
+    $documentation: "Base class of all AST nodes",
+    $propdoc: {
+        start: "[AST_Token] The first token of this node",
+        end: "[AST_Token] The last token of this node"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this);
+    },
+    walk: function(visitor) {
+        return this._walk(visitor); // not sure the indirection will be any help
+    }
+}, null);
+
+AST_Node.warn_function = null;
+AST_Node.warn = function(txt, props) {
+    if (AST_Node.warn_function)
+        AST_Node.warn_function(string_template(txt, props));
+};
+
+/* -----[ statements ]----- */
+
+var AST_Statement = DEFNODE("Statement", null, {
+    $documentation: "Base class of all statements",
+});
+
+var AST_Debugger = DEFNODE("Debugger", null, {
+    $documentation: "Represents a debugger statement",
+}, AST_Statement);
+
+var AST_Directive = DEFNODE("Directive", "value scope quote", {
+    $documentation: "Represents a directive, like \"use strict\";",
+    $propdoc: {
+        value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
+        scope: "[AST_Scope/S] The scope that this directive affects",
+        quote: "[string] the original quote character"
+    },
+}, AST_Statement);
+
+var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
+    $documentation: "A statement consisting of an expression, i.e. a = 1 + 2",
+    $propdoc: {
+        body: "[AST_Node] an expression node (should not be instanceof AST_Statement)"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.body._walk(visitor);
+        });
+    }
+}, AST_Statement);
+
+function walk_body(node, visitor) {
+    if (node.body instanceof AST_Statement) {
+        node.body._walk(visitor);
+    }
+    else node.body.forEach(function(stat){
+        stat._walk(visitor);
+    });
+};
+
+var AST_Block = DEFNODE("Block", "body", {
+    $documentation: "A body of statements (usually bracketed)",
+    $propdoc: {
+        body: "[AST_Statement*] an array of statements"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            walk_body(this, visitor);
+        });
+    }
+}, AST_Statement);
+
+var AST_BlockStatement = DEFNODE("BlockStatement", null, {
+    $documentation: "A block statement",
+}, AST_Block);
+
+var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
+    $documentation: "The empty statement (empty block or simply a semicolon)",
+    _walk: function(visitor) {
+        return visitor._visit(this);
+    }
+}, AST_Statement);
+
+var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
+    $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
+    $propdoc: {
+        body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.body._walk(visitor);
+        });
+    }
+}, AST_Statement);
+
+var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
+    $documentation: "Statement with a label",
+    $propdoc: {
+        label: "[AST_Label] a label definition"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.label._walk(visitor);
+            this.body._walk(visitor);
+        });
+    }
+}, AST_StatementWithBody);
+
+var AST_IterationStatement = DEFNODE("IterationStatement", null, {
+    $documentation: "Internal class.  All loops inherit from it."
+}, AST_StatementWithBody);
+
+var AST_DWLoop = DEFNODE("DWLoop", "condition", {
+    $documentation: "Base class for do/while statements",
+    $propdoc: {
+        condition: "[AST_Node] the loop condition.  Should not be instanceof AST_Statement"
+    }
+}, AST_IterationStatement);
+
+var AST_Do = DEFNODE("Do", null, {
+    $documentation: "A `do` statement",
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.body._walk(visitor);
+            this.condition._walk(visitor);
+        });
+    }
+}, AST_DWLoop);
+
+var AST_While = DEFNODE("While", null, {
+    $documentation: "A `while` statement",
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.condition._walk(visitor);
+            this.body._walk(visitor);
+        });
+    }
+}, AST_DWLoop);
+
+var AST_For = DEFNODE("For", "init condition step", {
+    $documentation: "A `for` statement",
+    $propdoc: {
+        init: "[AST_Node?] the `for` initialization code, or null if empty",
+        condition: "[AST_Node?] the `for` termination clause, or null if empty",
+        step: "[AST_Node?] the `for` update clause, or null if empty"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            if (this.init) this.init._walk(visitor);
+            if (this.condition) this.condition._walk(visitor);
+            if (this.step) this.step._walk(visitor);
+            this.body._walk(visitor);
+        });
+    }
+}, AST_IterationStatement);
+
+var AST_ForIn = DEFNODE("ForIn", "init name object", {
+    $documentation: "A `for ... in` statement",
+    $propdoc: {
+        init: "[AST_Node] the `for/in` initialization code",
+        name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
+        object: "[AST_Node] the object that we're looping through"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.init._walk(visitor);
+            this.object._walk(visitor);
+            this.body._walk(visitor);
+        });
+    }
+}, AST_IterationStatement);
+
+var AST_With = DEFNODE("With", "expression", {
+    $documentation: "A `with` statement",
+    $propdoc: {
+        expression: "[AST_Node] the `with` expression"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+            this.body._walk(visitor);
+        });
+    }
+}, AST_StatementWithBody);
+
+/* -----[ scope and functions ]----- */
+
+var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
+    $documentation: "Base class for all statements introducing a lexical scope",
+    $propdoc: {
+        directives: "[string*/S] an array of directives declared in this scope",
+        variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
+        functions: "[Object/S] like `variables`, but only lists function declarations",
+        uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
+        uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
+        parent_scope: "[AST_Scope?/S] link to the parent scope",
+        enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
+        cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
+    },
+}, AST_Block);
+
+var AST_Toplevel = DEFNODE("Toplevel", "globals", {
+    $documentation: "The toplevel scope",
+    $propdoc: {
+        globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
+    },
+    wrap_enclose: function(arg_parameter_pairs) {
+        var self = this;
+        var args = [];
+        var parameters = [];
+
+        arg_parameter_pairs.forEach(function(pair) {
+            var splitAt = pair.lastIndexOf(":");
+
+            args.push(pair.substr(0, splitAt));
+            parameters.push(pair.substr(splitAt + 1));
+        });
+
+        var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
+        wrapped_tl = parse(wrapped_tl);
+        wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
+            if (node instanceof AST_Directive && node.value == "$ORIG") {
+                return MAP.splice(self.body);
+            }
+        }));
+        return wrapped_tl;
+    },
+    wrap_commonjs: function(name, export_all) {
+        var self = this;
+        var to_export = [];
+        if (export_all) {
+            self.figure_out_scope();
+            self.walk(new TreeWalker(function(node){
+                if (node instanceof AST_SymbolDeclaration && node.definition().global) {
+                    if (!find_if(function(n){ return n.name == node.name }, to_export))
+                        to_export.push(node);
+                }
+            }));
+        }
+        var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))";
+        wrapped_tl = parse(wrapped_tl);
+        wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){
+            if (node instanceof AST_SimpleStatement) {
+                node = node.body;
+                if (node instanceof AST_String) switch (node.getValue()) {
+                  case "$ORIG":
+                    return MAP.splice(self.body);
+                  case "$EXPORTS":
+                    var body = [];
+                    to_export.forEach(function(sym){
+                        body.push(new AST_SimpleStatement({
+                            body: new AST_Assign({
+                                left: new AST_Sub({
+                                    expression: new AST_SymbolRef({ name: "exports" }),
+                                    property: new AST_String({ value: sym.name }),
+                                }),
+                                operator: "=",
+                                right: new AST_SymbolRef(sym),
+                            }),
+                        }));
+                    });
+                    return MAP.splice(body);
+                }
+            }
+        }));
+        return wrapped_tl;
+    }
+}, AST_Scope);
+
+var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
+    $documentation: "Base class for functions",
+    $propdoc: {
+        name: "[AST_SymbolDeclaration?] the name of this function",
+        argnames: "[AST_SymbolFunarg*] array of function arguments",
+        uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            if (this.name) this.name._walk(visitor);
+            this.argnames.forEach(function(arg){
+                arg._walk(visitor);
+            });
+            walk_body(this, visitor);
+        });
+    }
+}, AST_Scope);
+
+var AST_Accessor = DEFNODE("Accessor", null, {
+    $documentation: "A setter/getter function.  The `name` property is always null."
+}, AST_Lambda);
+
+var AST_Function = DEFNODE("Function", null, {
+    $documentation: "A function expression"
+}, AST_Lambda);
+
+var AST_Defun = DEFNODE("Defun", null, {
+    $documentation: "A function definition"
+}, AST_Lambda);
+
+/* -----[ JUMPS ]----- */
+
+var AST_Jump = DEFNODE("Jump", null, {
+    $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
+}, AST_Statement);
+
+var AST_Exit = DEFNODE("Exit", "value", {
+    $documentation: "Base class for “exits” (`return` and `throw`)",
+    $propdoc: {
+        value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, this.value && function(){
+            this.value._walk(visitor);
+        });
+    }
+}, AST_Jump);
+
+var AST_Return = DEFNODE("Return", null, {
+    $documentation: "A `return` statement"
+}, AST_Exit);
+
+var AST_Throw = DEFNODE("Throw", null, {
+    $documentation: "A `throw` statement"
+}, AST_Exit);
+
+var AST_LoopControl = DEFNODE("LoopControl", "label", {
+    $documentation: "Base class for loop control statements (`break` and `continue`)",
+    $propdoc: {
+        label: "[AST_LabelRef?] the label, or null if none",
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, this.label && function(){
+            this.label._walk(visitor);
+        });
+    }
+}, AST_Jump);
+
+var AST_Break = DEFNODE("Break", null, {
+    $documentation: "A `break` statement"
+}, AST_LoopControl);
+
+var AST_Continue = DEFNODE("Continue", null, {
+    $documentation: "A `continue` statement"
+}, AST_LoopControl);
+
+/* -----[ IF ]----- */
+
+var AST_If = DEFNODE("If", "condition alternative", {
+    $documentation: "A `if` statement",
+    $propdoc: {
+        condition: "[AST_Node] the `if` condition",
+        alternative: "[AST_Statement?] the `else` part, or null if not present"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.condition._walk(visitor);
+            this.body._walk(visitor);
+            if (this.alternative) this.alternative._walk(visitor);
+        });
+    }
+}, AST_StatementWithBody);
+
+/* -----[ SWITCH ]----- */
+
+var AST_Switch = DEFNODE("Switch", "expression", {
+    $documentation: "A `switch` statement",
+    $propdoc: {
+        expression: "[AST_Node] the `switch` “discriminant”"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+            walk_body(this, visitor);
+        });
+    }
+}, AST_Block);
+
+var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
+    $documentation: "Base class for `switch` branches",
+}, AST_Block);
+
+var AST_Default = DEFNODE("Default", null, {
+    $documentation: "A `default` switch branch",
+}, AST_SwitchBranch);
+
+var AST_Case = DEFNODE("Case", "expression", {
+    $documentation: "A `case` switch branch",
+    $propdoc: {
+        expression: "[AST_Node] the `case` expression"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+            walk_body(this, visitor);
+        });
+    }
+}, AST_SwitchBranch);
+
+/* -----[ EXCEPTIONS ]----- */
+
+var AST_Try = DEFNODE("Try", "bcatch bfinally", {
+    $documentation: "A `try` statement",
+    $propdoc: {
+        bcatch: "[AST_Catch?] the catch block, or null if not present",
+        bfinally: "[AST_Finally?] the finally block, or null if not present"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            walk_body(this, visitor);
+            if (this.bcatch) this.bcatch._walk(visitor);
+            if (this.bfinally) this.bfinally._walk(visitor);
+        });
+    }
+}, AST_Block);
+
+var AST_Catch = DEFNODE("Catch", "argname", {
+    $documentation: "A `catch` node; only makes sense as part of a `try` statement",
+    $propdoc: {
+        argname: "[AST_SymbolCatch] symbol for the exception"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.argname._walk(visitor);
+            walk_body(this, visitor);
+        });
+    }
+}, AST_Block);
+
+var AST_Finally = DEFNODE("Finally", null, {
+    $documentation: "A `finally` node; only makes sense as part of a `try` statement"
+}, AST_Block);
+
+/* -----[ VAR/CONST ]----- */
+
+var AST_Definitions = DEFNODE("Definitions", "definitions", {
+    $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)",
+    $propdoc: {
+        definitions: "[AST_VarDef*] array of variable definitions"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.definitions.forEach(function(def){
+                def._walk(visitor);
+            });
+        });
+    }
+}, AST_Statement);
+
+var AST_Var = DEFNODE("Var", null, {
+    $documentation: "A `var` statement"
+}, AST_Definitions);
+
+var AST_Const = DEFNODE("Const", null, {
+    $documentation: "A `const` statement"
+}, AST_Definitions);
+
+var AST_VarDef = DEFNODE("VarDef", "name value", {
+    $documentation: "A variable declaration; only appears in a AST_Definitions node",
+    $propdoc: {
+        name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
+        value: "[AST_Node?] initializer, or null of there's no initializer"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.name._walk(visitor);
+            if (this.value) this.value._walk(visitor);
+        });
+    }
+});
+
+/* -----[ OTHER ]----- */
+
+var AST_Call = DEFNODE("Call", "expression args", {
+    $documentation: "A function call expression",
+    $propdoc: {
+        expression: "[AST_Node] expression to invoke as function",
+        args: "[AST_Node*] array of arguments"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+            this.args.forEach(function(arg){
+                arg._walk(visitor);
+            });
+        });
+    }
+});
+
+var AST_New = DEFNODE("New", null, {
+    $documentation: "An object instantiation.  Derives from a function call since it has exactly the same properties"
+}, AST_Call);
+
+var AST_Seq = DEFNODE("Seq", "car cdr", {
+    $documentation: "A sequence expression (two comma-separated expressions)",
+    $propdoc: {
+        car: "[AST_Node] first element in sequence",
+        cdr: "[AST_Node] second element in sequence"
+    },
+    $cons: function(x, y) {
+        var seq = new AST_Seq(x);
+        seq.car = x;
+        seq.cdr = y;
+        return seq;
+    },
+    $from_array: function(array) {
+        if (array.length == 0) return null;
+        if (array.length == 1) return array[0].clone();
+        var list = null;
+        for (var i = array.length; --i >= 0;) {
+            list = AST_Seq.cons(array[i], list);
+        }
+        var p = list;
+        while (p) {
+            if (p.cdr && !p.cdr.cdr) {
+                p.cdr = p.cdr.car;
+                break;
+            }
+            p = p.cdr;
+        }
+        return list;
+    },
+    to_array: function() {
+        var p = this, a = [];
+        while (p) {
+            a.push(p.car);
+            if (p.cdr && !(p.cdr instanceof AST_Seq)) {
+                a.push(p.cdr);
+                break;
+            }
+            p = p.cdr;
+        }
+        return a;
+    },
+    add: function(node) {
+        var p = this;
+        while (p) {
+            if (!(p.cdr instanceof AST_Seq)) {
+                var cell = AST_Seq.cons(p.cdr, node);
+                return p.cdr = cell;
+            }
+            p = p.cdr;
+        }
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.car._walk(visitor);
+            if (this.cdr) this.cdr._walk(visitor);
+        });
+    }
+});
+
+var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
+    $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
+    $propdoc: {
+        expression: "[AST_Node] the “container” expression",
+        property: "[AST_Node|string] the property to access.  For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
+    }
+});
+
+var AST_Dot = DEFNODE("Dot", null, {
+    $documentation: "A dotted property access expression",
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+        });
+    }
+}, AST_PropAccess);
+
+var AST_Sub = DEFNODE("Sub", null, {
+    $documentation: "Index-style property access, i.e. `a[\"foo\"]`",
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+            this.property._walk(visitor);
+        });
+    }
+}, AST_PropAccess);
+
+var AST_Unary = DEFNODE("Unary", "operator expression", {
+    $documentation: "Base class for unary expressions",
+    $propdoc: {
+        operator: "[string] the operator",
+        expression: "[AST_Node] expression that this unary operator applies to"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.expression._walk(visitor);
+        });
+    }
+});
+
+var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, {
+    $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`"
+}, AST_Unary);
+
+var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
+    $documentation: "Unary postfix expression, i.e. `i++`"
+}, AST_Unary);
+
+var AST_Binary = DEFNODE("Binary", "left operator right", {
+    $documentation: "Binary expression, i.e. `a + b`",
+    $propdoc: {
+        left: "[AST_Node] left-hand side expression",
+        operator: "[string] the operator",
+        right: "[AST_Node] right-hand side expression"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.left._walk(visitor);
+            this.right._walk(visitor);
+        });
+    }
+});
+
+var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", {
+    $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`",
+    $propdoc: {
+        condition: "[AST_Node]",
+        consequent: "[AST_Node]",
+        alternative: "[AST_Node]"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.condition._walk(visitor);
+            this.consequent._walk(visitor);
+            this.alternative._walk(visitor);
+        });
+    }
+});
+
+var AST_Assign = DEFNODE("Assign", null, {
+    $documentation: "An assignment expression — `a = b + 5`",
+}, AST_Binary);
+
+/* -----[ LITERALS ]----- */
+
+var AST_Array = DEFNODE("Array", "elements", {
+    $documentation: "An array literal",
+    $propdoc: {
+        elements: "[AST_Node*] array of elements"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.elements.forEach(function(el){
+                el._walk(visitor);
+            });
+        });
+    }
+});
+
+var AST_Object = DEFNODE("Object", "properties", {
+    $documentation: "An object literal",
+    $propdoc: {
+        properties: "[AST_ObjectProperty*] array of properties"
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.properties.forEach(function(prop){
+                prop._walk(visitor);
+            });
+        });
+    }
+});
+
+var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
+    $documentation: "Base class for literal object properties",
+    $propdoc: {
+        key: "[string] the property name converted to a string for ObjectKeyVal.  For setters and getters this is an arbitrary AST_Node.",
+        value: "[AST_Node] property value.  For setters and getters this is an AST_Function."
+    },
+    _walk: function(visitor) {
+        return visitor._visit(this, function(){
+            this.value._walk(visitor);
+        });
+    }
+});
+
+var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
+    $documentation: "A key: value object property",
+    $propdoc: {
+        quote: "[string] the original quote character"
+    }
+}, AST_ObjectProperty);
+
+var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
+    $documentation: "An object setter property",
+}, AST_ObjectProperty);
+
+var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
+    $documentation: "An object getter property",
+}, AST_ObjectProperty);
+
+var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
+    $propdoc: {
+        name: "[string] name of this symbol",
+        scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
+        thedef: "[SymbolDef/S] the definition of this symbol"
+    },
+    $documentation: "Base class for all symbols",
+});
+
+var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
+    $documentation: "The name of a property accessor (setter/getter function)"
+}, AST_Symbol);
+
+var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
+    $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
+    $propdoc: {
+        init: "[AST_Node*/S] array of initializers for this declaration."
+    }
+}, AST_Symbol);
+
+var AST_SymbolVar = DEFNODE("SymbolVar", null, {
+    $documentation: "Symbol defining a variable",
+}, AST_SymbolDeclaration);
+
+var AST_SymbolConst = DEFNODE("SymbolConst", null, {
+    $documentation: "A constant declaration"
+}, AST_SymbolDeclaration);
+
+var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
+    $documentation: "Symbol naming a function argument",
+}, AST_SymbolVar);
+
+var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
+    $documentation: "Symbol defining a function",
+}, AST_SymbolDeclaration);
+
+var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
+    $documentation: "Symbol naming a function expression",
+}, AST_SymbolDeclaration);
+
+var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
+    $documentation: "Symbol naming the exception in catch",
+}, AST_SymbolDeclaration);
+
+var AST_Label = DEFNODE("Label", "references", {
+    $documentation: "Symbol naming a label (declaration)",
+    $propdoc: {
+        references: "[AST_LoopControl*] a list of nodes referring to this label"
+    },
+    initialize: function() {
+        this.references = [];
+        this.thedef = this;
+    }
+}, AST_Symbol);
+
+var AST_SymbolRef = DEFNODE("SymbolRef", null, {
+    $documentation: "Reference to some symbol (not definition/declaration)",
+}, AST_Symbol);
+
+var AST_LabelRef = DEFNODE("LabelRef", null, {
+    $documentation: "Reference to a label symbol",
+}, AST_Symbol);
+
+var AST_This = DEFNODE("This", null, {
+    $documentation: "The `this` symbol",
+}, AST_Symbol);
+
+var AST_Constant = DEFNODE("Constant", null, {
+    $documentation: "Base class for all constants",
+    getValue: function() {
+        return this.value;
+    }
+});
+
+var AST_String = DEFNODE("String", "value quote", {
+    $documentation: "A string literal",
+    $propdoc: {
+        value: "[string] the contents of this string",
+        quote: "[string] the original quote character"
+    }
+}, AST_Constant);
+
+var AST_Number = DEFNODE("Number", "value", {
+    $documentation: "A number literal",
+    $propdoc: {
+        value: "[number] the numeric value"
+    }
+}, AST_Constant);
+
+var AST_RegExp = DEFNODE("RegExp", "value", {
+    $documentation: "A regexp literal",
+    $propdoc: {
+        value: "[RegExp] the actual regexp"
+    }
+}, AST_Constant);
+
+var AST_Atom = DEFNODE("Atom", null, {
+    $documentation: "Base class for atoms",
+}, AST_Constant);
+
+var AST_Null = DEFNODE("Null", null, {
+    $documentation: "The `null` atom",
+    value: null
+}, AST_Atom);
+
+var AST_NaN = DEFNODE("NaN", null, {
+    $documentation: "The impossible value",
+    value: 0/0
+}, AST_Atom);
+
+var AST_Undefined = DEFNODE("Undefined", null, {
+    $documentation: "The `undefined` value",
+    value: (function(){}())
+}, AST_Atom);
+
+var AST_Hole = DEFNODE("Hole", null, {
+    $documentation: "A hole in an array",
+    value: (function(){}())
+}, AST_Atom);
+
+var AST_Infinity = DEFNODE("Infinity", null, {
+    $documentation: "The `Infinity` value",
+    value: 1/0
+}, AST_Atom);
+
+var AST_Boolean = DEFNODE("Boolean", null, {
+    $documentation: "Base class for booleans",
+}, AST_Atom);
+
+var AST_False = DEFNODE("False", null, {
+    $documentation: "The `false` atom",
+    value: false
+}, AST_Boolean);
+
+var AST_True = DEFNODE("True", null, {
+    $documentation: "The `true` atom",
+    value: true
+}, AST_Boolean);
+
+/* -----[ TreeWalker ]----- */
+
+function TreeWalker(callback) {
+    this.visit = callback;
+    this.stack = [];
+};
+TreeWalker.prototype = {
+    _visit: function(node, descend) {
+        this.stack.push(node);
+        var ret = this.visit(node, descend ? function(){
+            descend.call(node);
+        } : noop);
+        if (!ret && descend) {
+            descend.call(node);
+        }
+        this.stack.pop();
+        return ret;
+    },
+    parent: function(n) {
+        return this.stack[this.stack.length - 2 - (n || 0)];
+    },
+    push: function (node) {
+        this.stack.push(node);
+    },
+    pop: function() {
+        return this.stack.pop();
+    },
+    self: function() {
+        return this.stack[this.stack.length - 1];
+    },
+    find_parent: function(type) {
+        var stack = this.stack;
+        for (var i = stack.length; --i >= 0;) {
+            var x = stack[i];
+            if (x instanceof type) return x;
+        }
+    },
+    has_directive: function(type) {
+        return this.find_parent(AST_Scope).has_directive(type);
+    },
+    in_boolean_context: function() {
+        var stack = this.stack;
+        var i = stack.length, self = stack[--i];
+        while (i > 0) {
+            var p = stack[--i];
+            if ((p instanceof AST_If           && p.condition === self) ||
+                (p instanceof AST_Conditional  && p.condition === self) ||
+                (p instanceof AST_DWLoop       && p.condition === self) ||
+                (p instanceof AST_For          && p.condition === self) ||
+                (p instanceof AST_UnaryPrefix  && p.operator == "!" && p.expression === self))
+            {
+                return true;
+            }
+            if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
+                return false;
+            self = p;
+        }
+    },
+    loopcontrol_target: function(label) {
+        var stack = this.stack;
+        if (label) for (var i = stack.length; --i >= 0;) {
+            var x = stack[i];
+            if (x instanceof AST_LabeledStatement && x.label.name == label.name) {
+                return x.body;
+            }
+        } else for (var i = stack.length; --i >= 0;) {
+            var x = stack[i];
+            if (x instanceof AST_Switch || x instanceof AST_IterationStatement)
+                return x;
+        }
+    }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/compress.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/compress.js
new file mode 100644
index 0000000..4c4cf97
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/compress.js
@@ -0,0 +1,2515 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+function Compressor(options, false_by_default) {
+    if (!(this instanceof Compressor))
+        return new Compressor(options, false_by_default);
+    TreeTransformer.call(this, this.before, this.after);
+    this.options = defaults(options, {
+        sequences     : !false_by_default,
+        properties    : !false_by_default,
+        dead_code     : !false_by_default,
+        drop_debugger : !false_by_default,
+        unsafe        : false,
+        unsafe_comps  : false,
+        conditionals  : !false_by_default,
+        comparisons   : !false_by_default,
+        evaluate      : !false_by_default,
+        booleans      : !false_by_default,
+        loops         : !false_by_default,
+        unused        : !false_by_default,
+        hoist_funs    : !false_by_default,
+        keep_fargs    : false,
+        keep_fnames   : false,
+        hoist_vars    : false,
+        if_return     : !false_by_default,
+        join_vars     : !false_by_default,
+        cascade       : !false_by_default,
+        side_effects  : !false_by_default,
+        pure_getters  : false,
+        pure_funcs    : null,
+        negate_iife   : !false_by_default,
+        screw_ie8     : false,
+        drop_console  : false,
+        angular       : false,
+
+        warnings      : true,
+        global_defs   : {}
+    }, true);
+};
+
+Compressor.prototype = new TreeTransformer;
+merge(Compressor.prototype, {
+    option: function(key) { return this.options[key] },
+    warn: function() {
+        if (this.options.warnings)
+            AST_Node.warn.apply(AST_Node, arguments);
+    },
+    before: function(node, descend, in_list) {
+        if (node._squeezed) return node;
+        var was_scope = false;
+        if (node instanceof AST_Scope) {
+            node = node.hoist_declarations(this);
+            was_scope = true;
+        }
+        descend(node, this);
+        node = node.optimize(this);
+        if (was_scope && node instanceof AST_Scope) {
+            node.drop_unused(this);
+            descend(node, this);
+        }
+        node._squeezed = true;
+        return node;
+    }
+});
+
+(function(){
+
+    function OPT(node, optimizer) {
+        node.DEFMETHOD("optimize", function(compressor){
+            var self = this;
+            if (self._optimized) return self;
+            var opt = optimizer(self, compressor);
+            opt._optimized = true;
+            if (opt === self) return opt;
+            return opt.transform(compressor);
+        });
+    };
+
+    OPT(AST_Node, function(self, compressor){
+        return self;
+    });
+
+    AST_Node.DEFMETHOD("equivalent_to", function(node){
+        // XXX: this is a rather expensive way to test two node's equivalence:
+        return this.print_to_string() == node.print_to_string();
+    });
+
+    function make_node(ctor, orig, props) {
+        if (!props) props = {};
+        if (orig) {
+            if (!props.start) props.start = orig.start;
+            if (!props.end) props.end = orig.end;
+        }
+        return new ctor(props);
+    };
+
+    function make_node_from_constant(compressor, val, orig) {
+        // XXX: WIP.
+        // if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){
+        //     if (node instanceof AST_SymbolRef) {
+        //         var scope = compressor.find_parent(AST_Scope);
+        //         var def = scope.find_variable(node);
+        //         node.thedef = def;
+        //         return node;
+        //     }
+        // })).transform(compressor);
+
+        if (val instanceof AST_Node) return val.transform(compressor);
+        switch (typeof val) {
+          case "string":
+            return make_node(AST_String, orig, {
+                value: val
+            }).optimize(compressor);
+          case "number":
+            return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, {
+                value: val
+            }).optimize(compressor);
+          case "boolean":
+            return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
+          case "undefined":
+            return make_node(AST_Undefined, orig).optimize(compressor);
+          default:
+            if (val === null) {
+                return make_node(AST_Null, orig, { value: null }).optimize(compressor);
+            }
+            if (val instanceof RegExp) {
+                return make_node(AST_RegExp, orig, { value: val }).optimize(compressor);
+            }
+            throw new Error(string_template("Can't handle constant of type: {type}", {
+                type: typeof val
+            }));
+        }
+    };
+
+    function as_statement_array(thing) {
+        if (thing === null) return [];
+        if (thing instanceof AST_BlockStatement) return thing.body;
+        if (thing instanceof AST_EmptyStatement) return [];
+        if (thing instanceof AST_Statement) return [ thing ];
+        throw new Error("Can't convert thing to statement array");
+    };
+
+    function is_empty(thing) {
+        if (thing === null) return true;
+        if (thing instanceof AST_EmptyStatement) return true;
+        if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
+        return false;
+    };
+
+    function loop_body(x) {
+        if (x instanceof AST_Switch) return x;
+        if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
+            return (x.body instanceof AST_BlockStatement ? x.body : x);
+        }
+        return x;
+    };
+
+    function tighten_body(statements, compressor) {
+        var CHANGED;
+        do {
+            CHANGED = false;
+            if (compressor.option("angular")) {
+                statements = process_for_angular(statements);
+            }
+            statements = eliminate_spurious_blocks(statements);
+            if (compressor.option("dead_code")) {
+                statements = eliminate_dead_code(statements, compressor);
+            }
+            if (compressor.option("if_return")) {
+                statements = handle_if_return(statements, compressor);
+            }
+            if (compressor.option("sequences")) {
+                statements = sequencesize(statements, compressor);
+            }
+            if (compressor.option("join_vars")) {
+                statements = join_consecutive_vars(statements, compressor);
+            }
+        } while (CHANGED);
+
+        if (compressor.option("negate_iife")) {
+            negate_iifes(statements, compressor);
+        }
+
+        return statements;
+
+        function process_for_angular(statements) {
+            function has_inject(comment) {
+                return /@ngInject/.test(comment.value);
+            }
+            function make_arguments_names_list(func) {
+                return func.argnames.map(function(sym){
+                    return make_node(AST_String, sym, { value: sym.name });
+                });
+            }
+            function make_array(orig, elements) {
+                return make_node(AST_Array, orig, { elements: elements });
+            }
+            function make_injector(func, name) {
+                return make_node(AST_SimpleStatement, func, {
+                    body: make_node(AST_Assign, func, {
+                        operator: "=",
+                        left: make_node(AST_Dot, name, {
+                            expression: make_node(AST_SymbolRef, name, name),
+                            property: "$inject"
+                        }),
+                        right: make_array(func, make_arguments_names_list(func))
+                    })
+                });
+            }
+            function check_expression(body) {
+                if (body && body.args) {
+                    // if this is a function call check all of arguments passed
+                    body.args.forEach(function(argument, index, array) {
+                        var comments = argument.start.comments_before;
+                        // if the argument is function preceded by @ngInject
+                        if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) {
+                            // replace the function with an array of names of its parameters and function at the end
+                            array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument));
+                        }
+                    });
+                    // if this is chained call check previous one recursively
+                    if (body.expression && body.expression.expression) {
+                        check_expression(body.expression.expression);
+                    }
+                }
+            }
+            return statements.reduce(function(a, stat){
+                a.push(stat);
+
+                if (stat.body && stat.body.args) {
+                    check_expression(stat.body);
+                } else {
+                    var token = stat.start;
+                    var comments = token.comments_before;
+                    if (comments && comments.length > 0) {
+                        var last = comments.pop();
+                        if (has_inject(last)) {
+                            // case 1: defun
+                            if (stat instanceof AST_Defun) {
+                                a.push(make_injector(stat, stat.name));
+                            }
+                            else if (stat instanceof AST_Definitions) {
+                                stat.definitions.forEach(function(def) {
+                                    if (def.value && def.value instanceof AST_Lambda) {
+                                        a.push(make_injector(def.value, def.name));
+                                    }
+                                });
+                            }
+                            else {
+                                compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
+                            }
+                        }
+                    }
+                }
+
+                return a;
+            }, []);
+        }
+
+        function eliminate_spurious_blocks(statements) {
+            var seen_dirs = [];
+            return statements.reduce(function(a, stat){
+                if (stat instanceof AST_BlockStatement) {
+                    CHANGED = true;
+                    a.push.apply(a, eliminate_spurious_blocks(stat.body));
+                } else if (stat instanceof AST_EmptyStatement) {
+                    CHANGED = true;
+                } else if (stat instanceof AST_Directive) {
+                    if (seen_dirs.indexOf(stat.value) < 0) {
+                        a.push(stat);
+                        seen_dirs.push(stat.value);
+                    } else {
+                        CHANGED = true;
+                    }
+                } else {
+                    a.push(stat);
+                }
+                return a;
+            }, []);
+        };
+
+        function handle_if_return(statements, compressor) {
+            var self = compressor.self();
+            var in_lambda = self instanceof AST_Lambda;
+            var ret = [];
+            loop: for (var i = statements.length; --i >= 0;) {
+                var stat = statements[i];
+                switch (true) {
+                  case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
+                    CHANGED = true;
+                    // note, ret.length is probably always zero
+                    // because we drop unreachable code before this
+                    // step.  nevertheless, it's good to check.
+                    continue loop;
+                  case stat instanceof AST_If:
+                    if (stat.body instanceof AST_Return) {
+                        //---
+                        // pretty silly case, but:
+                        // if (foo()) return; return; ==> foo(); return;
+                        if (((in_lambda && ret.length == 0)
+                             || (ret[0] instanceof AST_Return && !ret[0].value))
+                            && !stat.body.value && !stat.alternative) {
+                            CHANGED = true;
+                            var cond = make_node(AST_SimpleStatement, stat.condition, {
+                                body: stat.condition
+                            });
+                            ret.unshift(cond);
+                            continue loop;
+                        }
+                        //---
+                        // if (foo()) return x; return y; ==> return foo() ? x : y;
+                        if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
+                            CHANGED = true;
+                            stat = stat.clone();
+                            stat.alternative = ret[0];
+                            ret[0] = stat.transform(compressor);
+                            continue loop;
+                        }
+                        //---
+                        // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
+                        if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) {
+                            CHANGED = true;
+                            stat = stat.clone();
+                            stat.alternative = ret[0] || make_node(AST_Return, stat, {
+                                value: make_node(AST_Undefined, stat)
+                            });
+                            ret[0] = stat.transform(compressor);
+                            continue loop;
+                        }
+                        //---
+                        // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
+                        if (!stat.body.value && in_lambda) {
+                            CHANGED = true;
+                            stat = stat.clone();
+                            stat.condition = stat.condition.negate(compressor);
+                            stat.body = make_node(AST_BlockStatement, stat, {
+                                body: as_statement_array(stat.alternative).concat(ret)
+                            });
+                            stat.alternative = null;
+                            ret = [ stat.transform(compressor) ];
+                            continue loop;
+                        }
+                        //---
+                        if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
+                            && (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
+                            CHANGED = true;
+                            ret.push(make_node(AST_Return, ret[0], {
+                                value: make_node(AST_Undefined, ret[0])
+                            }).transform(compressor));
+                            ret = as_statement_array(stat.alternative).concat(ret);
+                            ret.unshift(stat);
+                            continue loop;
+                        }
+                    }
+
+                    var ab = aborts(stat.body);
+                    var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
+                    if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
+                               || (ab instanceof AST_Continue && self === loop_body(lct))
+                               || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
+                        if (ab.label) {
+                            remove(ab.label.thedef.references, ab);
+                        }
+                        CHANGED = true;
+                        var body = as_statement_array(stat.body).slice(0, -1);
+                        stat = stat.clone();
+                        stat.condition = stat.condition.negate(compressor);
+                        stat.body = make_node(AST_BlockStatement, stat, {
+                            body: as_statement_array(stat.alternative).concat(ret)
+                        });
+                        stat.alternative = make_node(AST_BlockStatement, stat, {
+                            body: body
+                        });
+                        ret = [ stat.transform(compressor) ];
+                        continue loop;
+                    }
+
+                    var ab = aborts(stat.alternative);
+                    var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
+                    if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
+                               || (ab instanceof AST_Continue && self === loop_body(lct))
+                               || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
+                        if (ab.label) {
+                            remove(ab.label.thedef.references, ab);
+                        }
+                        CHANGED = true;
+                        stat = stat.clone();
+                        stat.body = make_node(AST_BlockStatement, stat.body, {
+                            body: as_statement_array(stat.body).concat(ret)
+                        });
+                        stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
+                            body: as_statement_array(stat.alternative).slice(0, -1)
+                        });
+                        ret = [ stat.transform(compressor) ];
+                        continue loop;
+                    }
+
+                    ret.unshift(stat);
+                    break;
+                  default:
+                    ret.unshift(stat);
+                    break;
+                }
+            }
+            return ret;
+        };
+
+        function eliminate_dead_code(statements, compressor) {
+            var has_quit = false;
+            var orig = statements.length;
+            var self = compressor.self();
+            statements = statements.reduce(function(a, stat){
+                if (has_quit) {
+                    extract_declarations_from_unreachable_code(compressor, stat, a);
+                } else {
+                    if (stat instanceof AST_LoopControl) {
+                        var lct = compressor.loopcontrol_target(stat.label);
+                        if ((stat instanceof AST_Break
+                             && lct instanceof AST_BlockStatement
+                             && loop_body(lct) === self) || (stat instanceof AST_Continue
+                                                             && loop_body(lct) === self)) {
+                            if (stat.label) {
+                                remove(stat.label.thedef.references, stat);
+                            }
+                        } else {
+                            a.push(stat);
+                        }
+                    } else {
+                        a.push(stat);
+                    }
+                    if (aborts(stat)) has_quit = true;
+                }
+                return a;
+            }, []);
+            CHANGED = statements.length != orig;
+            return statements;
+        };
+
+        function sequencesize(statements, compressor) {
+            if (statements.length < 2) return statements;
+            var seq = [], ret = [];
+            function push_seq() {
+                seq = AST_Seq.from_array(seq);
+                if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
+                    body: seq
+                }));
+                seq = [];
+            };
+            statements.forEach(function(stat){
+                if (stat instanceof AST_SimpleStatement && seq.length < 2000) seq.push(stat.body);
+                else push_seq(), ret.push(stat);
+            });
+            push_seq();
+            ret = sequencesize_2(ret, compressor);
+            CHANGED = ret.length != statements.length;
+            return ret;
+        };
+
+        function sequencesize_2(statements, compressor) {
+            function cons_seq(right) {
+                ret.pop();
+                var left = prev.body;
+                if (left instanceof AST_Seq) {
+                    left.add(right);
+                } else {
+                    left = AST_Seq.cons(left, right);
+                }
+                return left.transform(compressor);
+            };
+            var ret = [], prev = null;
+            statements.forEach(function(stat){
+                if (prev) {
+                    if (stat instanceof AST_For) {
+                        var opera = {};
+                        try {
+                            prev.body.walk(new TreeWalker(function(node){
+                                if (node instanceof AST_Binary && node.operator == "in")
+                                    throw opera;
+                            }));
+                            if (stat.init && !(stat.init instanceof AST_Definitions)) {
+                                stat.init = cons_seq(stat.init);
+                            }
+                            else if (!stat.init) {
+                                stat.init = prev.body;
+                                ret.pop();
+                            }
+                        } catch(ex) {
+                            if (ex !== opera) throw ex;
+                        }
+                    }
+                    else if (stat instanceof AST_If) {
+                        stat.condition = cons_seq(stat.condition);
+                    }
+                    else if (stat instanceof AST_With) {
+                        stat.expression = cons_seq(stat.expression);
+                    }
+                    else if (stat instanceof AST_Exit && stat.value) {
+                        stat.value = cons_seq(stat.value);
+                    }
+                    else if (stat instanceof AST_Exit) {
+                        stat.value = cons_seq(make_node(AST_Undefined, stat));
+                    }
+                    else if (stat instanceof AST_Switch) {
+                        stat.expression = cons_seq(stat.expression);
+                    }
+                }
+                ret.push(stat);
+                prev = stat instanceof AST_SimpleStatement ? stat : null;
+            });
+            return ret;
+        };
+
+        function join_consecutive_vars(statements, compressor) {
+            var prev = null;
+            return statements.reduce(function(a, stat){
+                if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
+                    prev.definitions = prev.definitions.concat(stat.definitions);
+                    CHANGED = true;
+                }
+                else if (stat instanceof AST_For
+                         && prev instanceof AST_Definitions
+                         && (!stat.init || stat.init.TYPE == prev.TYPE)) {
+                    CHANGED = true;
+                    a.pop();
+                    if (stat.init) {
+                        stat.init.definitions = prev.definitions.concat(stat.init.definitions);
+                    } else {
+                        stat.init = prev;
+                    }
+                    a.push(stat);
+                    prev = stat;
+                }
+                else {
+                    prev = stat;
+                    a.push(stat);
+                }
+                return a;
+            }, []);
+        };
+
+        function negate_iifes(statements, compressor) {
+            statements.forEach(function(stat){
+                if (stat instanceof AST_SimpleStatement) {
+                    stat.body = (function transform(thing) {
+                        return thing.transform(new TreeTransformer(function(node){
+                            if (node instanceof AST_Call && node.expression instanceof AST_Function) {
+                                return make_node(AST_UnaryPrefix, node, {
+                                    operator: "!",
+                                    expression: node
+                                });
+                            }
+                            else if (node instanceof AST_Call) {
+                                node.expression = transform(node.expression);
+                            }
+                            else if (node instanceof AST_Seq) {
+                                node.car = transform(node.car);
+                            }
+                            else if (node instanceof AST_Conditional) {
+                                var expr = transform(node.condition);
+                                if (expr !== node.condition) {
+                                    // it has been negated, reverse
+                                    node.condition = expr;
+                                    var tmp = node.consequent;
+                                    node.consequent = node.alternative;
+                                    node.alternative = tmp;
+                                }
+                            }
+                            return node;
+                        }));
+                    })(stat.body);
+                }
+            });
+        };
+
+    };
+
+    function extract_declarations_from_unreachable_code(compressor, stat, target) {
+        compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
+        stat.walk(new TreeWalker(function(node){
+            if (node instanceof AST_Definitions) {
+                compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
+                node.remove_initializers();
+                target.push(node);
+                return true;
+            }
+            if (node instanceof AST_Defun) {
+                target.push(node);
+                return true;
+            }
+            if (node instanceof AST_Scope) {
+                return true;
+            }
+        }));
+    };
+
+    /* -----[ boolean/negation helpers ]----- */
+
+    // methods to determine whether an expression has a boolean result type
+    (function (def){
+        var unary_bool = [ "!", "delete" ];
+        var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
+        def(AST_Node, function(){ return false });
+        def(AST_UnaryPrefix, function(){
+            return member(this.operator, unary_bool);
+        });
+        def(AST_Binary, function(){
+            return member(this.operator, binary_bool) ||
+                ( (this.operator == "&&" || this.operator == "||") &&
+                  this.left.is_boolean() && this.right.is_boolean() );
+        });
+        def(AST_Conditional, function(){
+            return this.consequent.is_boolean() && this.alternative.is_boolean();
+        });
+        def(AST_Assign, function(){
+            return this.operator == "=" && this.right.is_boolean();
+        });
+        def(AST_Seq, function(){
+            return this.cdr.is_boolean();
+        });
+        def(AST_True, function(){ return true });
+        def(AST_False, function(){ return true });
+    })(function(node, func){
+        node.DEFMETHOD("is_boolean", func);
+    });
+
+    // methods to determine if an expression has a string result type
+    (function (def){
+        def(AST_Node, function(){ return false });
+        def(AST_String, function(){ return true });
+        def(AST_UnaryPrefix, function(){
+            return this.operator == "typeof";
+        });
+        def(AST_Binary, function(compressor){
+            return this.operator == "+" &&
+                (this.left.is_string(compressor) || this.right.is_string(compressor));
+        });
+        def(AST_Assign, function(compressor){
+            return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
+        });
+        def(AST_Seq, function(compressor){
+            return this.cdr.is_string(compressor);
+        });
+        def(AST_Conditional, function(compressor){
+            return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
+        });
+        def(AST_Call, function(compressor){
+            return compressor.option("unsafe")
+                && this.expression instanceof AST_SymbolRef
+                && this.expression.name == "String"
+                && this.expression.undeclared();
+        });
+    })(function(node, func){
+        node.DEFMETHOD("is_string", func);
+    });
+
+    function best_of(ast1, ast2) {
+        return ast1.print_to_string().length >
+            ast2.print_to_string().length
+            ? ast2 : ast1;
+    };
+
+    // methods to evaluate a constant expression
+    (function (def){
+        // The evaluate method returns an array with one or two
+        // elements.  If the node has been successfully reduced to a
+        // constant, then the second element tells us the value;
+        // otherwise the second element is missing.  The first element
+        // of the array is always an AST_Node descendant; if
+        // evaluation was successful it's a node that represents the
+        // constant; otherwise it's the original or a replacement node.
+        AST_Node.DEFMETHOD("evaluate", function(compressor){
+            if (!compressor.option("evaluate")) return [ this ];
+            try {
+                var val = this._eval(compressor);
+                return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
+            } catch(ex) {
+                if (ex !== def) throw ex;
+                return [ this ];
+            }
+        });
+        def(AST_Statement, function(){
+            throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
+        });
+        def(AST_Function, function(){
+            // XXX: AST_Function inherits from AST_Scope, which itself
+            // inherits from AST_Statement; however, an AST_Function
+            // isn't really a statement.  This could byte in other
+            // places too. :-( Wish JS had multiple inheritance.
+            throw def;
+        });
+        function ev(node, compressor) {
+            if (!compressor) throw new Error("Compressor must be passed");
+
+            return node._eval(compressor);
+        };
+        def(AST_Node, function(){
+            throw def;          // not constant
+        });
+        def(AST_Constant, function(){
+            return this.getValue();
+        });
+        def(AST_UnaryPrefix, function(compressor){
+            var e = this.expression;
+            switch (this.operator) {
+              case "!": return !ev(e, compressor);
+              case "typeof":
+                // Function would be evaluated to an array and so typeof would
+                // incorrectly return 'object'. Hence making is a special case.
+                if (e instanceof AST_Function) return typeof function(){};
+
+                e = ev(e, compressor);
+
+                // typeof <RegExp> returns "object" or "function" on different platforms
+                // so cannot evaluate reliably
+                if (e instanceof RegExp) throw def;
+
+                return typeof e;
+              case "void": return void ev(e, compressor);
+              case "~": return ~ev(e, compressor);
+              case "-":
+                e = ev(e, compressor);
+                if (e === 0) throw def;
+                return -e;
+              case "+": return +ev(e, compressor);
+            }
+            throw def;
+        });
+        def(AST_Binary, function(c){
+            var left = this.left, right = this.right;
+            switch (this.operator) {
+              case "&&"         : return ev(left, c) &&         ev(right, c);
+              case "||"         : return ev(left, c) ||         ev(right, c);
+              case "|"          : return ev(left, c) |          ev(right, c);
+              case "&"          : return ev(left, c) &          ev(right, c);
+              case "^"          : return ev(left, c) ^          ev(right, c);
+              case "+"          : return ev(left, c) +          ev(right, c);
+              case "*"          : return ev(left, c) *          ev(right, c);
+              case "/"          : return ev(left, c) /          ev(right, c);
+              case "%"          : return ev(left, c) %          ev(right, c);
+              case "-"          : return ev(left, c) -          ev(right, c);
+              case "<<"         : return ev(left, c) <<         ev(right, c);
+              case ">>"         : return ev(left, c) >>         ev(right, c);
+              case ">>>"        : return ev(left, c) >>>        ev(right, c);
+              case "=="         : return ev(left, c) ==         ev(right, c);
+              case "==="        : return ev(left, c) ===        ev(right, c);
+              case "!="         : return ev(left, c) !=         ev(right, c);
+              case "!=="        : return ev(left, c) !==        ev(right, c);
+              case "<"          : return ev(left, c) <          ev(right, c);
+              case "<="         : return ev(left, c) <=         ev(right, c);
+              case ">"          : return ev(left, c) >          ev(right, c);
+              case ">="         : return ev(left, c) >=         ev(right, c);
+              case "in"         : return ev(left, c) in         ev(right, c);
+              case "instanceof" : return ev(left, c) instanceof ev(right, c);
+            }
+            throw def;
+        });
+        def(AST_Conditional, function(compressor){
+            return ev(this.condition, compressor)
+                ? ev(this.consequent, compressor)
+                : ev(this.alternative, compressor);
+        });
+        def(AST_SymbolRef, function(compressor){
+            var d = this.definition();
+            if (d && d.constant && d.init) return ev(d.init, compressor);
+            throw def;
+        });
+        def(AST_Dot, function(compressor){
+            if (compressor.option("unsafe") && this.property == "length") {
+                var str = ev(this.expression, compressor);
+                if (typeof str == "string")
+                    return str.length;
+            }
+            throw def;
+        });
+    })(function(node, func){
+        node.DEFMETHOD("_eval", func);
+    });
+
+    // method to negate an expression
+    (function(def){
+        function basic_negation(exp) {
+            return make_node(AST_UnaryPrefix, exp, {
+                operator: "!",
+                expression: exp
+            });
+        };
+        def(AST_Node, function(){
+            return basic_negation(this);
+        });
+        def(AST_Statement, function(){
+            throw new Error("Cannot negate a statement");
+        });
+        def(AST_Function, function(){
+            return basic_negation(this);
+        });
+        def(AST_UnaryPrefix, function(){
+            if (this.operator == "!")
+                return this.expression;
+            return basic_negation(this);
+        });
+        def(AST_Seq, function(compressor){
+            var self = this.clone();
+            self.cdr = self.cdr.negate(compressor);
+            return self;
+        });
+        def(AST_Conditional, function(compressor){
+            var self = this.clone();
+            self.consequent = self.consequent.negate(compressor);
+            self.alternative = self.alternative.negate(compressor);
+            return best_of(basic_negation(this), self);
+        });
+        def(AST_Binary, function(compressor){
+            var self = this.clone(), op = this.operator;
+            if (compressor.option("unsafe_comps")) {
+                switch (op) {
+                  case "<=" : self.operator = ">"  ; return self;
+                  case "<"  : self.operator = ">=" ; return self;
+                  case ">=" : self.operator = "<"  ; return self;
+                  case ">"  : self.operator = "<=" ; return self;
+                }
+            }
+            switch (op) {
+              case "==" : self.operator = "!="; return self;
+              case "!=" : self.operator = "=="; return self;
+              case "===": self.operator = "!=="; return self;
+              case "!==": self.operator = "==="; return self;
+              case "&&":
+                self.operator = "||";
+                self.left = self.left.negate(compressor);
+                self.right = self.right.negate(compressor);
+                return best_of(basic_negation(this), self);
+              case "||":
+                self.operator = "&&";
+                self.left = self.left.negate(compressor);
+                self.right = self.right.negate(compressor);
+                return best_of(basic_negation(this), self);
+            }
+            return basic_negation(this);
+        });
+    })(function(node, func){
+        node.DEFMETHOD("negate", function(compressor){
+            return func.call(this, compressor);
+        });
+    });
+
+    // determine if expression has side effects
+    (function(def){
+        def(AST_Node, function(compressor){ return true });
+
+        def(AST_EmptyStatement, function(compressor){ return false });
+        def(AST_Constant, function(compressor){ return false });
+        def(AST_This, function(compressor){ return false });
+
+        def(AST_Call, function(compressor){
+            var pure = compressor.option("pure_funcs");
+            if (!pure) return true;
+            return pure.indexOf(this.expression.print_to_string()) < 0;
+        });
+
+        def(AST_Block, function(compressor){
+            for (var i = this.body.length; --i >= 0;) {
+                if (this.body[i].has_side_effects(compressor))
+                    return true;
+            }
+            return false;
+        });
+
+        def(AST_SimpleStatement, function(compressor){
+            return this.body.has_side_effects(compressor);
+        });
+        def(AST_Defun, function(compressor){ return true });
+        def(AST_Function, function(compressor){ return false });
+        def(AST_Binary, function(compressor){
+            return this.left.has_side_effects(compressor)
+                || this.right.has_side_effects(compressor);
+        });
+        def(AST_Assign, function(compressor){ return true });
+        def(AST_Conditional, function(compressor){
+            return this.condition.has_side_effects(compressor)
+                || this.consequent.has_side_effects(compressor)
+                || this.alternative.has_side_effects(compressor);
+        });
+        def(AST_Unary, function(compressor){
+            return this.operator == "delete"
+                || this.operator == "++"
+                || this.operator == "--"
+                || this.expression.has_side_effects(compressor);
+        });
+        def(AST_SymbolRef, function(compressor){
+            return this.global() && this.undeclared();
+        });
+        def(AST_Object, function(compressor){
+            for (var i = this.properties.length; --i >= 0;)
+                if (this.properties[i].has_side_effects(compressor))
+                    return true;
+            return false;
+        });
+        def(AST_ObjectProperty, function(compressor){
+            return this.value.has_side_effects(compressor);
+        });
+        def(AST_Array, function(compressor){
+            for (var i = this.elements.length; --i >= 0;)
+                if (this.elements[i].has_side_effects(compressor))
+                    return true;
+            return false;
+        });
+        def(AST_Dot, function(compressor){
+            if (!compressor.option("pure_getters")) return true;
+            return this.expression.has_side_effects(compressor);
+        });
+        def(AST_Sub, function(compressor){
+            if (!compressor.option("pure_getters")) return true;
+            return this.expression.has_side_effects(compressor)
+                || this.property.has_side_effects(compressor);
+        });
+        def(AST_PropAccess, function(compressor){
+            return !compressor.option("pure_getters");
+        });
+        def(AST_Seq, function(compressor){
+            return this.car.has_side_effects(compressor)
+                || this.cdr.has_side_effects(compressor);
+        });
+    })(function(node, func){
+        node.DEFMETHOD("has_side_effects", func);
+    });
+
+    // tell me if a statement aborts
+    function aborts(thing) {
+        return thing && thing.aborts();
+    };
+    (function(def){
+        def(AST_Statement, function(){ return null });
+        def(AST_Jump, function(){ return this });
+        function block_aborts(){
+            var n = this.body.length;
+            return n > 0 && aborts(this.body[n - 1]);
+        };
+        def(AST_BlockStatement, block_aborts);
+        def(AST_SwitchBranch, block_aborts);
+        def(AST_If, function(){
+            return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
+        });
+    })(function(node, func){
+        node.DEFMETHOD("aborts", func);
+    });
+
+    /* -----[ optimizers ]----- */
+
+    OPT(AST_Directive, function(self, compressor){
+        if (self.scope.has_directive(self.value) !== self.scope) {
+            return make_node(AST_EmptyStatement, self);
+        }
+        return self;
+    });
+
+    OPT(AST_Debugger, function(self, compressor){
+        if (compressor.option("drop_debugger"))
+            return make_node(AST_EmptyStatement, self);
+        return self;
+    });
+
+    OPT(AST_LabeledStatement, function(self, compressor){
+        if (self.body instanceof AST_Break
+            && compressor.loopcontrol_target(self.body.label) === self.body) {
+            return make_node(AST_EmptyStatement, self);
+        }
+        return self.label.references.length == 0 ? self.body : self;
+    });
+
+    OPT(AST_Block, function(self, compressor){
+        self.body = tighten_body(self.body, compressor);
+        return self;
+    });
+
+    OPT(AST_BlockStatement, function(self, compressor){
+        self.body = tighten_body(self.body, compressor);
+        switch (self.body.length) {
+          case 1: return self.body[0];
+          case 0: return make_node(AST_EmptyStatement, self);
+        }
+        return self;
+    });
+
+    AST_Scope.DEFMETHOD("drop_unused", function(compressor){
+        var self = this;
+        if (compressor.option("unused")
+            && !(self instanceof AST_Toplevel)
+            && !self.uses_eval
+           ) {
+            var in_use = [];
+            var initializations = new Dictionary();
+            // pass 1: find out which symbols are directly used in
+            // this scope (not in nested scopes).
+            var scope = this;
+            var tw = new TreeWalker(function(node, descend){
+                if (node !== self) {
+                    if (node instanceof AST_Defun) {
+                        initializations.add(node.name.name, node);
+                        return true; // don't go in nested scopes
+                    }
+                    if (node instanceof AST_Definitions && scope === self) {
+                        node.definitions.forEach(function(def){
+                            if (def.value) {
+                                initializations.add(def.name.name, def.value);
+                                if (def.value.has_side_effects(compressor)) {
+                                    def.value.walk(tw);
+                                }
+                            }
+                        });
+                        return true;
+                    }
+                    if (node instanceof AST_SymbolRef) {
+                        push_uniq(in_use, node.definition());
+                        return true;
+                    }
+                    if (node instanceof AST_Scope) {
+                        var save_scope = scope;
+                        scope = node;
+                        descend();
+                        scope = save_scope;
+                        return true;
+                    }
+                }
+            });
+            self.walk(tw);
+            // pass 2: for every used symbol we need to walk its
+            // initialization code to figure out if it uses other
+            // symbols (that may not be in_use).
+            for (var i = 0; i < in_use.length; ++i) {
+                in_use[i].orig.forEach(function(decl){
+                    // undeclared globals will be instanceof AST_SymbolRef
+                    var init = initializations.get(decl.name);
+                    if (init) init.forEach(function(init){
+                        var tw = new TreeWalker(function(node){
+                            if (node instanceof AST_SymbolRef) {
+                                push_uniq(in_use, node.definition());
+                            }
+                        });
+                        init.walk(tw);
+                    });
+                });
+            }
+            // pass 3: we should drop declarations not in_use
+            var tt = new TreeTransformer(
+                function before(node, descend, in_list) {
+                    if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
+                        if (compressor.option("unsafe") && !compressor.option("keep_fargs")) {
+                            for (var a = node.argnames, i = a.length; --i >= 0;) {
+                                var sym = a[i];
+                                if (sym.unreferenced()) {
+                                    a.pop();
+                                    compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
+                                        name : sym.name,
+                                        file : sym.start.file,
+                                        line : sym.start.line,
+                                        col  : sym.start.col
+                                    });
+                                }
+                                else break;
+                            }
+                        }
+                    }
+                    if (node instanceof AST_Defun && node !== self) {
+                        if (!member(node.name.definition(), in_use)) {
+                            compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
+                                name : node.name.name,
+                                file : node.name.start.file,
+                                line : node.name.start.line,
+                                col  : node.name.start.col
+                            });
+                            return make_node(AST_EmptyStatement, node);
+                        }
+                        return node;
+                    }
+                    if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
+                        var def = node.definitions.filter(function(def){
+                            if (member(def.name.definition(), in_use)) return true;
+                            var w = {
+                                name : def.name.name,
+                                file : def.name.start.file,
+                                line : def.name.start.line,
+                                col  : def.name.start.col
+                            };
+                            if (def.value && def.value.has_side_effects(compressor)) {
+                                def._unused_side_effects = true;
+                                compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
+                                return true;
+                            }
+                            compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
+                            return false;
+                        });
+                        // place uninitialized names at the start
+                        def = mergeSort(def, function(a, b){
+                            if (!a.value && b.value) return -1;
+                            if (!b.value && a.value) return 1;
+                            return 0;
+                        });
+                        // for unused names whose initialization has
+                        // side effects, we can cascade the init. code
+                        // into the next one, or next statement.
+                        var side_effects = [];
+                        for (var i = 0; i < def.length;) {
+                            var x = def[i];
+                            if (x._unused_side_effects) {
+                                side_effects.push(x.value);
+                                def.splice(i, 1);
+                            } else {
+                                if (side_effects.length > 0) {
+                                    side_effects.push(x.value);
+                                    x.value = AST_Seq.from_array(side_effects);
+                                    side_effects = [];
+                                }
+                                ++i;
+                            }
+                        }
+                        if (side_effects.length > 0) {
+                            side_effects = make_node(AST_BlockStatement, node, {
+                                body: [ make_node(AST_SimpleStatement, node, {
+                                    body: AST_Seq.from_array(side_effects)
+                                }) ]
+                            });
+                        } else {
+                            side_effects = null;
+                        }
+                        if (def.length == 0 && !side_effects) {
+                            return make_node(AST_EmptyStatement, node);
+                        }
+                        if (def.length == 0) {
+                            return in_list ? MAP.splice(side_effects.body) : side_effects;
+                        }
+                        node.definitions = def;
+                        if (side_effects) {
+                            side_effects.body.unshift(node);
+                            return in_list ? MAP.splice(side_effects.body) : side_effects;
+                        }
+                        return node;
+                    }
+                    if (node instanceof AST_For) {
+                        descend(node, this);
+
+                        if (node.init instanceof AST_BlockStatement) {
+                            // certain combination of unused name + side effect leads to:
+                            //    https://github.com/mishoo/UglifyJS2/issues/44
+                            // that's an invalid AST.
+                            // We fix it at this stage by moving the `var` outside the `for`.
+
+                            var body = node.init.body.slice(0, -1);
+                            node.init = node.init.body.slice(-1)[0].body;
+                            body.push(node);
+
+                            return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
+                                body: body
+                            });
+                        }
+                    }
+                    if (node instanceof AST_Scope && node !== self)
+                        return node;
+                }
+            );
+            self.transform(tt);
+        }
+    });
+
+    AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
+        var hoist_funs = compressor.option("hoist_funs");
+        var hoist_vars = compressor.option("hoist_vars");
+        var self = this;
+        if (hoist_funs || hoist_vars) {
+            var dirs = [];
+            var hoisted = [];
+            var vars = new Dictionary(), vars_found = 0, var_decl = 0;
+            // let's count var_decl first, we seem to waste a lot of
+            // space if we hoist `var` when there's only one.
+            self.walk(new TreeWalker(function(node){
+                if (node instanceof AST_Scope && node !== self)
+                    return true;
+                if (node instanceof AST_Var) {
+                    ++var_decl;
+                    return true;
+                }
+            }));
+            hoist_vars = hoist_vars && var_decl > 1;
+            var tt = new TreeTransformer(
+                function before(node) {
+                    if (node !== self) {
+                        if (node instanceof AST_Directive) {
+                            dirs.push(node);
+                            return make_node(AST_EmptyStatement, node);
+                        }
+                        if (node instanceof AST_Defun && hoist_funs) {
+                            hoisted.push(node);
+                            return make_node(AST_EmptyStatement, node);
+                        }
+                        if (node instanceof AST_Var && hoist_vars) {
+                            node.definitions.forEach(function(def){
+                                vars.set(def.name.name, def);
+                                ++vars_found;
+                            });
+                            var seq = node.to_assignments();
+                            var p = tt.parent();
+                            if (p instanceof AST_ForIn && p.init === node) {
+                                if (seq == null) return node.definitions[0].name;
+                                return seq;
+                            }
+                            if (p instanceof AST_For && p.init === node) {
+                                return seq;
+                            }
+                            if (!seq) return make_node(AST_EmptyStatement, node);
+                            return make_node(AST_SimpleStatement, node, {
+                                body: seq
+                            });
+                        }
+                        if (node instanceof AST_Scope)
+                            return node; // to avoid descending in nested scopes
+                    }
+                }
+            );
+            self = self.transform(tt);
+            if (vars_found > 0) {
+                // collect only vars which don't show up in self's arguments list
+                var defs = [];
+                vars.each(function(def, name){
+                    if (self instanceof AST_Lambda
+                        && find_if(function(x){ return x.name == def.name.name },
+                                   self.argnames)) {
+                        vars.del(name);
+                    } else {
+                        def = def.clone();
+                        def.value = null;
+                        defs.push(def);
+                        vars.set(name, def);
+                    }
+                });
+                if (defs.length > 0) {
+                    // try to merge in assignments
+                    for (var i = 0; i < self.body.length;) {
+                        if (self.body[i] instanceof AST_SimpleStatement) {
+                            var expr = self.body[i].body, sym, assign;
+                            if (expr instanceof AST_Assign
+                                && expr.operator == "="
+                                && (sym = expr.left) instanceof AST_Symbol
+                                && vars.has(sym.name))
+                            {
+                                var def = vars.get(sym.name);
+                                if (def.value) break;
+                                def.value = expr.right;
+                                remove(defs, def);
+                                defs.push(def);
+                                self.body.splice(i, 1);
+                                continue;
+                            }
+                            if (expr instanceof AST_Seq
+                                && (assign = expr.car) instanceof AST_Assign
+                                && assign.operator == "="
+                                && (sym = assign.left) instanceof AST_Symbol
+                                && vars.has(sym.name))
+                            {
+                                var def = vars.get(sym.name);
+                                if (def.value) break;
+                                def.value = assign.right;
+                                remove(defs, def);
+                                defs.push(def);
+                                self.body[i].body = expr.cdr;
+                                continue;
+                            }
+                        }
+                        if (self.body[i] instanceof AST_EmptyStatement) {
+                            self.body.splice(i, 1);
+                            continue;
+                        }
+                        if (self.body[i] instanceof AST_BlockStatement) {
+                            var tmp = [ i, 1 ].concat(self.body[i].body);
+                            self.body.splice.apply(self.body, tmp);
+                            continue;
+                        }
+                        break;
+                    }
+                    defs = make_node(AST_Var, self, {
+                        definitions: defs
+                    });
+                    hoisted.push(defs);
+                };
+            }
+            self.body = dirs.concat(hoisted, self.body);
+        }
+        return self;
+    });
+
+    OPT(AST_SimpleStatement, function(self, compressor){
+        if (compressor.option("side_effects")) {
+            if (!self.body.has_side_effects(compressor)) {
+                compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
+                return make_node(AST_EmptyStatement, self);
+            }
+        }
+        return self;
+    });
+
+    OPT(AST_DWLoop, function(self, compressor){
+        var cond = self.condition.evaluate(compressor);
+        self.condition = cond[0];
+        if (!compressor.option("loops")) return self;
+        if (cond.length > 1) {
+            if (cond[1]) {
+                return make_node(AST_For, self, {
+                    body: self.body
+                });
+            } else if (self instanceof AST_While) {
+                if (compressor.option("dead_code")) {
+                    var a = [];
+                    extract_declarations_from_unreachable_code(compressor, self.body, a);
+                    return make_node(AST_BlockStatement, self, { body: a });
+                }
+            }
+        }
+        return self;
+    });
+
+    function if_break_in_loop(self, compressor) {
+        function drop_it(rest) {
+            rest = as_statement_array(rest);
+            if (self.body instanceof AST_BlockStatement) {
+                self.body = self.body.clone();
+                self.body.body = rest.concat(self.body.body.slice(1));
+                self.body = self.body.transform(compressor);
+            } else {
+                self.body = make_node(AST_BlockStatement, self.body, {
+                    body: rest
+                }).transform(compressor);
+            }
+            if_break_in_loop(self, compressor);
+        }
+        var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
+        if (first instanceof AST_If) {
+            if (first.body instanceof AST_Break
+                && compressor.loopcontrol_target(first.body.label) === self) {
+                if (self.condition) {
+                    self.condition = make_node(AST_Binary, self.condition, {
+                        left: self.condition,
+                        operator: "&&",
+                        right: first.condition.negate(compressor),
+                    });
+                } else {
+                    self.condition = first.condition.negate(compressor);
+                }
+                drop_it(first.alternative);
+            }
+            else if (first.alternative instanceof AST_Break
+                     && compressor.loopcontrol_target(first.alternative.label) === self) {
+                if (self.condition) {
+                    self.condition = make_node(AST_Binary, self.condition, {
+                        left: self.condition,
+                        operator: "&&",
+                        right: first.condition,
+                    });
+                } else {
+                    self.condition = first.condition;
+                }
+                drop_it(first.body);
+            }
+        }
+    };
+
+    OPT(AST_While, function(self, compressor) {
+        if (!compressor.option("loops")) return self;
+        self = AST_DWLoop.prototype.optimize.call(self, compressor);
+        if (self instanceof AST_While) {
+            if_break_in_loop(self, compressor);
+            self = make_node(AST_For, self, self).transform(compressor);
+        }
+        return self;
+    });
+
+    OPT(AST_For, function(self, compressor){
+        var cond = self.condition;
+        if (cond) {
+            cond = cond.evaluate(compressor);
+            self.condition = cond[0];
+        }
+        if (!compressor.option("loops")) return self;
+        if (cond) {
+            if (cond.length > 1 && !cond[1]) {
+                if (compressor.option("dead_code")) {
+                    var a = [];
+                    if (self.init instanceof AST_Statement) {
+                        a.push(self.init);
+                    }
+                    else if (self.init) {
+                        a.push(make_node(AST_SimpleStatement, self.init, {
+                            body: self.init
+                        }));
+                    }
+                    extract_declarations_from_unreachable_code(compressor, self.body, a);
+                    return make_node(AST_BlockStatement, self, { body: a });
+                }
+            }
+        }
+        if_break_in_loop(self, compressor);
+        return self;
+    });
+
+    OPT(AST_If, function(self, compressor){
+        if (!compressor.option("conditionals")) return self;
+        // if condition can be statically determined, warn and drop
+        // one of the blocks.  note, statically determined implies
+        // “has no side effects”; also it doesn't work for cases like
+        // `x && true`, though it probably should.
+        var cond = self.condition.evaluate(compressor);
+        self.condition = cond[0];
+        if (cond.length > 1) {
+            if (cond[1]) {
+                compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
+                if (compressor.option("dead_code")) {
+                    var a = [];
+                    if (self.alternative) {
+                        extract_declarations_from_unreachable_code(compressor, self.alternative, a);
+                    }
+                    a.push(self.body);
+                    return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
+                }
+            } else {
+                compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
+                if (compressor.option("dead_code")) {
+                    var a = [];
+                    extract_declarations_from_unreachable_code(compressor, self.body, a);
+                    if (self.alternative) a.push(self.alternative);
+                    return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
+                }
+            }
+        }
+        if (is_empty(self.alternative)) self.alternative = null;
+        var negated = self.condition.negate(compressor);
+        var negated_is_best = best_of(self.condition, negated) === negated;
+        if (self.alternative && negated_is_best) {
+            negated_is_best = false; // because we already do the switch here.
+            self.condition = negated;
+            var tmp = self.body;
+            self.body = self.alternative || make_node(AST_EmptyStatement);
+            self.alternative = tmp;
+        }
+        if (is_empty(self.body) && is_empty(self.alternative)) {
+            return make_node(AST_SimpleStatement, self.condition, {
+                body: self.condition
+            }).transform(compressor);
+        }
+        if (self.body instanceof AST_SimpleStatement
+            && self.alternative instanceof AST_SimpleStatement) {
+            return make_node(AST_SimpleStatement, self, {
+                body: make_node(AST_Conditional, self, {
+                    condition   : self.condition,
+                    consequent  : self.body.body,
+                    alternative : self.alternative.body
+                })
+            }).transform(compressor);
+        }
+        if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
+            if (negated_is_best) return make_node(AST_SimpleStatement, self, {
+                body: make_node(AST_Binary, self, {
+                    operator : "||",
+                    left     : negated,
+                    right    : self.body.body
+                })
+            }).transform(compressor);
+            return make_node(AST_SimpleStatement, self, {
+                body: make_node(AST_Binary, self, {
+                    operator : "&&",
+                    left     : self.condition,
+                    right    : self.body.body
+                })
+            }).transform(compressor);
+        }
+        if (self.body instanceof AST_EmptyStatement
+            && self.alternative
+            && self.alternative instanceof AST_SimpleStatement) {
+            return make_node(AST_SimpleStatement, self, {
+                body: make_node(AST_Binary, self, {
+                    operator : "||",
+                    left     : self.condition,
+                    right    : self.alternative.body
+                })
+            }).transform(compressor);
+        }
+        if (self.body instanceof AST_Exit
+            && self.alternative instanceof AST_Exit
+            && self.body.TYPE == self.alternative.TYPE) {
+            return make_node(self.body.CTOR, self, {
+                value: make_node(AST_Conditional, self, {
+                    condition   : self.condition,
+                    consequent  : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
+                    alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
+                })
+            }).transform(compressor);
+        }
+        if (self.body instanceof AST_If
+            && !self.body.alternative
+            && !self.alternative) {
+            self.condition = make_node(AST_Binary, self.condition, {
+                operator: "&&",
+                left: self.condition,
+                right: self.body.condition
+            }).transform(compressor);
+            self.body = self.body.body;
+        }
+        if (aborts(self.body)) {
+            if (self.alternative) {
+                var alt = self.alternative;
+                self.alternative = null;
+                return make_node(AST_BlockStatement, self, {
+                    body: [ self, alt ]
+                }).transform(compressor);
+            }
+        }
+        if (aborts(self.alternative)) {
+            var body = self.body;
+            self.body = self.alternative;
+            self.condition = negated_is_best ? negated : self.condition.negate(compressor);
+            self.alternative = null;
+            return make_node(AST_BlockStatement, self, {
+                body: [ self, body ]
+            }).transform(compressor);
+        }
+        return self;
+    });
+
+    OPT(AST_Switch, function(self, compressor){
+        if (self.body.length == 0 && compressor.option("conditionals")) {
+            return make_node(AST_SimpleStatement, self, {
+                body: self.expression
+            }).transform(compressor);
+        }
+        for(;;) {
+            var last_branch = self.body[self.body.length - 1];
+            if (last_branch) {
+                var stat = last_branch.body[last_branch.body.length - 1]; // last statement
+                if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
+                    last_branch.body.pop();
+                if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
+                    self.body.pop();
+                    continue;
+                }
+            }
+            break;
+        }
+        var exp = self.expression.evaluate(compressor);
+        out: if (exp.length == 2) try {
+            // constant expression
+            self.expression = exp[0];
+            if (!compressor.option("dead_code")) break out;
+            var value = exp[1];
+            var in_if = false;
+            var in_block = false;
+            var started = false;
+            var stopped = false;
+            var ruined = false;
+            var tt = new TreeTransformer(function(node, descend, in_list){
+                if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
+                    // no need to descend these node types
+                    return node;
+                }
+                else if (node instanceof AST_Switch && node === self) {
+                    node = node.clone();
+                    descend(node, this);
+                    return ruined ? node : make_node(AST_BlockStatement, node, {
+                        body: node.body.reduce(function(a, branch){
+                            return a.concat(branch.body);
+                        }, [])
+                    }).transform(compressor);
+                }
+                else if (node instanceof AST_If || node instanceof AST_Try) {
+                    var save = in_if;
+                    in_if = !in_block;
+                    descend(node, this);
+                    in_if = save;
+                    return node;
+                }
+                else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
+                    var save = in_block;
+                    in_block = true;
+                    descend(node, this);
+                    in_block = save;
+                    return node;
+                }
+                else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
+                    if (in_if) {
+                        ruined = true;
+                        return node;
+                    }
+                    if (in_block) return node;
+                    stopped = true;
+                    return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
+                }
+                else if (node instanceof AST_SwitchBranch && this.parent() === self) {
+                    if (stopped) return MAP.skip;
+                    if (node instanceof AST_Case) {
+                        var exp = node.expression.evaluate(compressor);
+                        if (exp.length < 2) {
+                            // got a case with non-constant expression, baling out
+                            throw self;
+                        }
+                        if (exp[1] === value || started) {
+                            started = true;
+                            if (aborts(node)) stopped = true;
+                            descend(node, this);
+                            return node;
+                        }
+                        return MAP.skip;
+                    }
+                    descend(node, this);
+                    return node;
+                }
+            });
+            tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
+            self = self.transform(tt);
+        } catch(ex) {
+            if (ex !== self) throw ex;
+        }
+        return self;
+    });
+
+    OPT(AST_Case, function(self, compressor){
+        self.body = tighten_body(self.body, compressor);
+        return self;
+    });
+
+    OPT(AST_Try, function(self, compressor){
+        self.body = tighten_body(self.body, compressor);
+        return self;
+    });
+
+    AST_Definitions.DEFMETHOD("remove_initializers", function(){
+        this.definitions.forEach(function(def){ def.value = null });
+    });
+
+    AST_Definitions.DEFMETHOD("to_assignments", function(){
+        var assignments = this.definitions.reduce(function(a, def){
+            if (def.value) {
+                var name = make_node(AST_SymbolRef, def.name, def.name);
+                a.push(make_node(AST_Assign, def, {
+                    operator : "=",
+                    left     : name,
+                    right    : def.value
+                }));
+            }
+            return a;
+        }, []);
+        if (assignments.length == 0) return null;
+        return AST_Seq.from_array(assignments);
+    });
+
+    OPT(AST_Definitions, function(self, compressor){
+        if (self.definitions.length == 0)
+            return make_node(AST_EmptyStatement, self);
+        return self;
+    });
+
+    OPT(AST_Function, function(self, compressor){
+        self = AST_Lambda.prototype.optimize.call(self, compressor);
+        if (compressor.option("unused") && !compressor.option("keep_fnames")) {
+            if (self.name && self.name.unreferenced()) {
+                self.name = null;
+            }
+        }
+        return self;
+    });
+
+    OPT(AST_Call, function(self, compressor){
+        if (compressor.option("unsafe")) {
+            var exp = self.expression;
+            if (exp instanceof AST_SymbolRef && exp.undeclared()) {
+                switch (exp.name) {
+                  case "Array":
+                    if (self.args.length != 1) {
+                        return make_node(AST_Array, self, {
+                            elements: self.args
+                        }).transform(compressor);
+                    }
+                    break;
+                  case "Object":
+                    if (self.args.length == 0) {
+                        return make_node(AST_Object, self, {
+                            properties: []
+                        });
+                    }
+                    break;
+                  case "String":
+                    if (self.args.length == 0) return make_node(AST_String, self, {
+                        value: ""
+                    });
+                    if (self.args.length <= 1) return make_node(AST_Binary, self, {
+                        left: self.args[0],
+                        operator: "+",
+                        right: make_node(AST_String, self, { value: "" })
+                    }).transform(compressor);
+                    break;
+                  case "Number":
+                    if (self.args.length == 0) return make_node(AST_Number, self, {
+                        value: 0
+                    });
+                    if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
+                        expression: self.args[0],
+                        operator: "+"
+                    }).transform(compressor);
+                  case "Boolean":
+                    if (self.args.length == 0) return make_node(AST_False, self);
+                    if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
+                        expression: make_node(AST_UnaryPrefix, null, {
+                            expression: self.args[0],
+                            operator: "!"
+                        }),
+                        operator: "!"
+                    }).transform(compressor);
+                    break;
+                  case "Function":
+                    // new Function() => function(){}
+                    if (self.args.length == 0) return make_node(AST_Function, self, {
+                        argnames: [],
+                        body: []
+                    });
+                    if (all(self.args, function(x){ return x instanceof AST_String })) {
+                        // quite a corner-case, but we can handle it:
+                        //   https://github.com/mishoo/UglifyJS2/issues/203
+                        // if the code argument is a constant, then we can minify it.
+                        try {
+                            var code = "(function(" + self.args.slice(0, -1).map(function(arg){
+                                return arg.value;
+                            }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
+                            var ast = parse(code);
+                            ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
+                            var comp = new Compressor(compressor.options);
+                            ast = ast.transform(comp);
+                            ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
+                            ast.mangle_names();
+                            var fun;
+                            try {
+                                ast.walk(new TreeWalker(function(node){
+                                    if (node instanceof AST_Lambda) {
+                                        fun = node;
+                                        throw ast;
+                                    }
+                                }));
+                            } catch(ex) {
+                                if (ex !== ast) throw ex;
+                            };
+                            if (!fun) return self;
+                            var args = fun.argnames.map(function(arg, i){
+                                return make_node(AST_String, self.args[i], {
+                                    value: arg.print_to_string()
+                                });
+                            });
+                            var code = OutputStream();
+                            AST_BlockStatement.prototype._codegen.call(fun, fun, code);
+                            code = code.toString().replace(/^\{|\}$/g, "");
+                            args.push(make_node(AST_String, self.args[self.args.length - 1], {
+                                value: code
+                            }));
+                            self.args = args;
+                            return self;
+                        } catch(ex) {
+                            if (ex instanceof JS_Parse_Error) {
+                                compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
+                                compressor.warn(ex.toString());
+                            } else {
+                                console.log(ex);
+                                throw ex;
+                            }
+                        }
+                    }
+                    break;
+                }
+            }
+            else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
+                return make_node(AST_Binary, self, {
+                    left: make_node(AST_String, self, { value: "" }),
+                    operator: "+",
+                    right: exp.expression
+                }).transform(compressor);
+            }
+            else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
+                var separator = self.args.length == 0 ? "," : self.args[0].evaluate(compressor)[1];
+                if (separator == null) break EXIT; // not a constant
+                var elements = exp.expression.elements.reduce(function(a, el){
+                    el = el.evaluate(compressor);
+                    if (a.length == 0 || el.length == 1) {
+                        a.push(el);
+                    } else {
+                        var last = a[a.length - 1];
+                        if (last.length == 2) {
+                            // it's a constant
+                            var val = "" + last[1] + separator + el[1];
+                            a[a.length - 1] = [ make_node_from_constant(compressor, val, last[0]), val ];
+                        } else {
+                            a.push(el);
+                        }
+                    }
+                    return a;
+                }, []);
+                if (elements.length == 0) return make_node(AST_String, self, { value: "" });
+                if (elements.length == 1) return elements[0][0];
+                if (separator == "") {
+                    var first;
+                    if (elements[0][0] instanceof AST_String
+                        || elements[1][0] instanceof AST_String) {
+                        first = elements.shift()[0];
+                    } else {
+                        first = make_node(AST_String, self, { value: "" });
+                    }
+                    return elements.reduce(function(prev, el){
+                        return make_node(AST_Binary, el[0], {
+                            operator : "+",
+                            left     : prev,
+                            right    : el[0],
+                        });
+                    }, first).transform(compressor);
+                }
+                // need this awkward cloning to not affect original element
+                // best_of will decide which one to get through.
+                var node = self.clone();
+                node.expression = node.expression.clone();
+                node.expression.expression = node.expression.expression.clone();
+                node.expression.expression.elements = elements.map(function(el){
+                    return el[0];
+                });
+                return best_of(self, node);
+            }
+        }
+        if (compressor.option("side_effects")) {
+            if (self.expression instanceof AST_Function
+                && self.args.length == 0
+                && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) {
+                return make_node(AST_Undefined, self).transform(compressor);
+            }
+        }
+        if (compressor.option("drop_console")) {
+            if (self.expression instanceof AST_PropAccess) {
+                var name = self.expression.expression;
+                while (name.expression) {
+                    name = name.expression;
+                }
+                if (name instanceof AST_SymbolRef
+                    && name.name == "console"
+                    && name.undeclared()) {
+                    return make_node(AST_Undefined, self).transform(compressor);
+                }
+            }
+        }
+        return self.evaluate(compressor)[0];
+    });
+
+    OPT(AST_New, function(self, compressor){
+        if (compressor.option("unsafe")) {
+            var exp = self.expression;
+            if (exp instanceof AST_SymbolRef && exp.undeclared()) {
+                switch (exp.name) {
+                  case "Object":
+                  case "RegExp":
+                  case "Function":
+                  case "Error":
+                  case "Array":
+                    return make_node(AST_Call, self, self).transform(compressor);
+                }
+            }
+        }
+        return self;
+    });
+
+    OPT(AST_Seq, function(self, compressor){
+        if (!compressor.option("side_effects"))
+            return self;
+        if (!self.car.has_side_effects(compressor)) {
+            // we shouldn't compress (1,eval)(something) to
+            // eval(something) because that changes the meaning of
+            // eval (becomes lexical instead of global).
+            var p;
+            if (!(self.cdr instanceof AST_SymbolRef
+                  && self.cdr.name == "eval"
+                  && self.cdr.undeclared()
+                  && (p = compressor.parent()) instanceof AST_Call
+                  && p.expression === self)) {
+                return self.cdr;
+            }
+        }
+        if (compressor.option("cascade")) {
+            if (self.car instanceof AST_Assign
+                && !self.car.left.has_side_effects(compressor)) {
+                if (self.car.left.equivalent_to(self.cdr)) {
+                    return self.car;
+                }
+                if (self.cdr instanceof AST_Call
+                    && self.cdr.expression.equivalent_to(self.car.left)) {
+                    self.cdr.expression = self.car;
+                    return self.cdr;
+                }
+            }
+            if (!self.car.has_side_effects(compressor)
+                && !self.cdr.has_side_effects(compressor)
+                && self.car.equivalent_to(self.cdr)) {
+                return self.car;
+            }
+        }
+        if (self.cdr instanceof AST_UnaryPrefix
+            && self.cdr.operator == "void"
+            && !self.cdr.expression.has_side_effects(compressor)) {
+            self.cdr.expression = self.car;
+            return self.cdr;
+        }
+        if (self.cdr instanceof AST_Undefined) {
+            return make_node(AST_UnaryPrefix, self, {
+                operator   : "void",
+                expression : self.car
+            });
+        }
+        return self;
+    });
+
+    AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
+        if (compressor.option("sequences")) {
+            if (this.expression instanceof AST_Seq) {
+                var seq = this.expression;
+                var x = seq.to_array();
+                this.expression = x.pop();
+                x.push(this);
+                seq = AST_Seq.from_array(x).transform(compressor);
+                return seq;
+            }
+        }
+        return this;
+    });
+
+    OPT(AST_UnaryPostfix, function(self, compressor){
+        return self.lift_sequences(compressor);
+    });
+
+    OPT(AST_UnaryPrefix, function(self, compressor){
+        self = self.lift_sequences(compressor);
+        var e = self.expression;
+        if (compressor.option("booleans") && compressor.in_boolean_context()) {
+            switch (self.operator) {
+              case "!":
+                if (e instanceof AST_UnaryPrefix && e.operator == "!") {
+                    // !!foo ==> foo, if we're in boolean context
+                    return e.expression;
+                }
+                break;
+              case "typeof":
+                // typeof always returns a non-empty string, thus it's
+                // always true in booleans
+                compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
+                return make_node(AST_True, self);
+            }
+            if (e instanceof AST_Binary && self.operator == "!") {
+                self = best_of(self, e.negate(compressor));
+            }
+        }
+        return self.evaluate(compressor)[0];
+    });
+
+    function has_side_effects_or_prop_access(node, compressor) {
+        var save_pure_getters = compressor.option("pure_getters");
+        compressor.options.pure_getters = false;
+        var ret = node.has_side_effects(compressor);
+        compressor.options.pure_getters = save_pure_getters;
+        return ret;
+    }
+
+    AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
+        if (compressor.option("sequences")) {
+            if (this.left instanceof AST_Seq) {
+                var seq = this.left;
+                var x = seq.to_array();
+                this.left = x.pop();
+                x.push(this);
+                seq = AST_Seq.from_array(x).transform(compressor);
+                return seq;
+            }
+            if (this.right instanceof AST_Seq
+                && this instanceof AST_Assign
+                && !has_side_effects_or_prop_access(this.left, compressor)) {
+                var seq = this.right;
+                var x = seq.to_array();
+                this.right = x.pop();
+                x.push(this);
+                seq = AST_Seq.from_array(x).transform(compressor);
+                return seq;
+            }
+        }
+        return this;
+    });
+
+    var commutativeOperators = makePredicate("== === != !== * & | ^");
+
+    OPT(AST_Binary, function(self, compressor){
+        var reverse = compressor.has_directive("use asm") ? noop
+            : function(op, force) {
+                if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) {
+                    if (op) self.operator = op;
+                    var tmp = self.left;
+                    self.left = self.right;
+                    self.right = tmp;
+                }
+            };
+        if (commutativeOperators(self.operator)) {
+            if (self.right instanceof AST_Constant
+                && !(self.left instanceof AST_Constant)) {
+                // if right is a constant, whatever side effects the
+                // left side might have could not influence the
+                // result.  hence, force switch.
+
+                if (!(self.left instanceof AST_Binary
+                      && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
+                    reverse(null, true);
+                }
+            }
+            if (/^[!=]==?$/.test(self.operator)) {
+                if (self.left instanceof AST_SymbolRef && self.right instanceof AST_Conditional) {
+                    if (self.right.consequent instanceof AST_SymbolRef
+                        && self.right.consequent.definition() === self.left.definition()) {
+                        if (/^==/.test(self.operator)) return self.right.condition;
+                        if (/^!=/.test(self.operator)) return self.right.condition.negate(compressor);
+                    }
+                    if (self.right.alternative instanceof AST_SymbolRef
+                        && self.right.alternative.definition() === self.left.definition()) {
+                        if (/^==/.test(self.operator)) return self.right.condition.negate(compressor);
+                        if (/^!=/.test(self.operator)) return self.right.condition;
+                    }
+                }
+                if (self.right instanceof AST_SymbolRef && self.left instanceof AST_Conditional) {
+                    if (self.left.consequent instanceof AST_SymbolRef
+                        && self.left.consequent.definition() === self.right.definition()) {
+                        if (/^==/.test(self.operator)) return self.left.condition;
+                        if (/^!=/.test(self.operator)) return self.left.condition.negate(compressor);
+                    }
+                    if (self.left.alternative instanceof AST_SymbolRef
+                        && self.left.alternative.definition() === self.right.definition()) {
+                        if (/^==/.test(self.operator)) return self.left.condition.negate(compressor);
+                        if (/^!=/.test(self.operator)) return self.left.condition;
+                    }
+                }
+            }
+        }
+        self = self.lift_sequences(compressor);
+        if (compressor.option("comparisons")) switch (self.operator) {
+          case "===":
+          case "!==":
+            if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
+                (self.left.is_boolean() && self.right.is_boolean())) {
+                self.operator = self.operator.substr(0, 2);
+            }
+            // XXX: intentionally falling down to the next case
+          case "==":
+          case "!=":
+            if (self.left instanceof AST_String
+                && self.left.value == "undefined"
+                && self.right instanceof AST_UnaryPrefix
+                && self.right.operator == "typeof"
+                && compressor.option("unsafe")) {
+                if (!(self.right.expression instanceof AST_SymbolRef)
+                    || !self.right.expression.undeclared()) {
+                    self.right = self.right.expression;
+                    self.left = make_node(AST_Undefined, self.left).optimize(compressor);
+                    if (self.operator.length == 2) self.operator += "=";
+                }
+            }
+            break;
+        }
+        if (compressor.option("conditionals")) {
+            if (self.operator == "&&") {
+                var ll = self.left.evaluate(compressor);
+                var rr = self.right.evaluate(compressor);
+                if (ll.length > 1) {
+                    if (ll[1]) {
+                        compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
+                        return rr[0];
+                    } else {
+                        compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
+                        return ll[0];
+                    }
+                }
+            }
+            else if (self.operator == "||") {
+                var ll = self.left.evaluate(compressor);
+                var rr = self.right.evaluate(compressor);
+                if (ll.length > 1) {
+                    if (ll[1]) {
+                        compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
+                        return ll[0];
+                    } else {
+                        compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
+                        return rr[0];
+                    }
+                }
+            }
+        }
+        if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
+          case "&&":
+            var ll = self.left.evaluate(compressor);
+            var rr = self.right.evaluate(compressor);
+            if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
+                compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
+                if (self.left.has_side_effects(compressor)) {
+                    return make_node(AST_Seq, self, {
+                        car: self.left,
+                        cdr: make_node(AST_False)
+                    }).optimize(compressor);
+                }
+                return make_node(AST_False, self);
+            }
+            if (ll.length > 1 && ll[1]) {
+                return rr[0];
+            }
+            if (rr.length > 1 && rr[1]) {
+                return ll[0];
+            }
+            break;
+          case "||":
+            var ll = self.left.evaluate(compressor);
+            var rr = self.right.evaluate(compressor);
+            if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
+                compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
+                if (self.left.has_side_effects(compressor)) {
+                    return make_node(AST_Seq, self, {
+                        car: self.left,
+                        cdr: make_node(AST_True)
+                    }).optimize(compressor);
+                }
+                return make_node(AST_True, self);
+            }
+            if (ll.length > 1 && !ll[1]) {
+                return rr[0];
+            }
+            if (rr.length > 1 && !rr[1]) {
+                return ll[0];
+            }
+            break;
+          case "+":
+            var ll = self.left.evaluate(compressor);
+            var rr = self.right.evaluate(compressor);
+            if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) ||
+                (rr.length > 1 && rr[0] instanceof AST_String && rr[1])) {
+                compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
+                return make_node(AST_True, self);
+            }
+            break;
+        }
+        if (compressor.option("comparisons") && self.is_boolean()) {
+            if (!(compressor.parent() instanceof AST_Binary)
+                || compressor.parent() instanceof AST_Assign) {
+                var negated = make_node(AST_UnaryPrefix, self, {
+                    operator: "!",
+                    expression: self.negate(compressor)
+                });
+                self = best_of(self, negated);
+            }
+            switch (self.operator) {
+              case "<": reverse(">"); break;
+              case "<=": reverse(">="); break;
+            }
+        }
+        if (self.operator == "+" && self.right instanceof AST_String
+            && self.right.getValue() === "" && self.left instanceof AST_Binary
+            && self.left.operator == "+" && self.left.is_string(compressor)) {
+            return self.left;
+        }
+        if (compressor.option("evaluate")) {
+            if (self.operator == "+") {
+                if (self.left instanceof AST_Constant
+                    && self.right instanceof AST_Binary
+                    && self.right.operator == "+"
+                    && self.right.left instanceof AST_Constant
+                    && self.right.is_string(compressor)) {
+                    self = make_node(AST_Binary, self, {
+                        operator: "+",
+                        left: make_node(AST_String, null, {
+                            value: "" + self.left.getValue() + self.right.left.getValue(),
+                            start: self.left.start,
+                            end: self.right.left.end
+                        }),
+                        right: self.right.right
+                    });
+                }
+                if (self.right instanceof AST_Constant
+                    && self.left instanceof AST_Binary
+                    && self.left.operator == "+"
+                    && self.left.right instanceof AST_Constant
+                    && self.left.is_string(compressor)) {
+                    self = make_node(AST_Binary, self, {
+                        operator: "+",
+                        left: self.left.left,
+                        right: make_node(AST_String, null, {
+                            value: "" + self.left.right.getValue() + self.right.getValue(),
+                            start: self.left.right.start,
+                            end: self.right.end
+                        })
+                    });
+                }
+                if (self.left instanceof AST_Binary
+                    && self.left.operator == "+"
+                    && self.left.is_string(compressor)
+                    && self.left.right instanceof AST_Constant
+                    && self.right instanceof AST_Binary
+                    && self.right.operator == "+"
+                    && self.right.left instanceof AST_Constant
+                    && self.right.is_string(compressor)) {
+                    self = make_node(AST_Binary, self, {
+                        operator: "+",
+                        left: make_node(AST_Binary, self.left, {
+                            operator: "+",
+                            left: self.left.left,
+                            right: make_node(AST_String, null, {
+                                value: "" + self.left.right.getValue() + self.right.left.getValue(),
+                                start: self.left.right.start,
+                                end: self.right.left.end
+                            })
+                        }),
+                        right: self.right.right
+                    });
+                }
+            }
+        }
+        // x && (y && z)  ==>  x && y && z
+        // x || (y || z)  ==>  x || y || z
+        if (self.right instanceof AST_Binary
+            && self.right.operator == self.operator
+            && (self.operator == "&&" || self.operator == "||"))
+        {
+            self.left = make_node(AST_Binary, self.left, {
+                operator : self.operator,
+                left     : self.left,
+                right    : self.right.left
+            });
+            self.right = self.right.right;
+            return self.transform(compressor);
+        }
+        return self.evaluate(compressor)[0];
+    });
+
+    OPT(AST_SymbolRef, function(self, compressor){
+        if (self.undeclared()) {
+            var defines = compressor.option("global_defs");
+            if (defines && defines.hasOwnProperty(self.name)) {
+                return make_node_from_constant(compressor, defines[self.name], self);
+            }
+            switch (self.name) {
+              case "undefined":
+                return make_node(AST_Undefined, self);
+              case "NaN":
+                return make_node(AST_NaN, self).transform(compressor);
+              case "Infinity":
+                return make_node(AST_Infinity, self).transform(compressor);
+            }
+        }
+        return self;
+    });
+
+    OPT(AST_Infinity, function (self, compressor) {
+        return make_node(AST_Binary, self, {
+            operator : '/',
+            left     : make_node(AST_Number, self, {value: 1}),
+            right    : make_node(AST_Number, self, {value: 0})
+        });
+    });
+
+    OPT(AST_Undefined, function(self, compressor){
+        if (compressor.option("unsafe")) {
+            var scope = compressor.find_parent(AST_Scope);
+            var undef = scope.find_variable("undefined");
+            if (undef) {
+                var ref = make_node(AST_SymbolRef, self, {
+                    name   : "undefined",
+                    scope  : scope,
+                    thedef : undef
+                });
+                ref.reference();
+                return ref;
+            }
+        }
+        return self;
+    });
+
+    var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
+    OPT(AST_Assign, function(self, compressor){
+        self = self.lift_sequences(compressor);
+        if (self.operator == "="
+            && self.left instanceof AST_SymbolRef
+            && self.right instanceof AST_Binary
+            && self.right.left instanceof AST_SymbolRef
+            && self.right.left.name == self.left.name
+            && member(self.right.operator, ASSIGN_OPS)) {
+            self.operator = self.right.operator + "=";
+            self.right = self.right.right;
+        }
+        return self;
+    });
+
+    OPT(AST_Conditional, function(self, compressor){
+        if (!compressor.option("conditionals")) return self;
+        if (self.condition instanceof AST_Seq) {
+            var car = self.condition.car;
+            self.condition = self.condition.cdr;
+            return AST_Seq.cons(car, self);
+        }
+        var cond = self.condition.evaluate(compressor);
+        if (cond.length > 1) {
+            if (cond[1]) {
+                compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
+                return self.consequent;
+            } else {
+                compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
+                return self.alternative;
+            }
+        }
+        var negated = cond[0].negate(compressor);
+        if (best_of(cond[0], negated) === negated) {
+            self = make_node(AST_Conditional, self, {
+                condition: negated,
+                consequent: self.alternative,
+                alternative: self.consequent
+            });
+        }
+        var consequent = self.consequent;
+        var alternative = self.alternative;
+        if (consequent instanceof AST_Assign
+            && alternative instanceof AST_Assign
+            && consequent.operator == alternative.operator
+            && consequent.left.equivalent_to(alternative.left)
+            && !consequent.left.has_side_effects(compressor)
+           ) {
+            /*
+             * Stuff like this:
+             * if (foo) exp = something; else exp = something_else;
+             * ==>
+             * exp = foo ? something : something_else;
+             */
+            return make_node(AST_Assign, self, {
+                operator: consequent.operator,
+                left: consequent.left,
+                right: make_node(AST_Conditional, self, {
+                    condition: self.condition,
+                    consequent: consequent.right,
+                    alternative: alternative.right
+                })
+            });
+        }
+        if (consequent instanceof AST_Call
+            && alternative.TYPE === consequent.TYPE
+            && consequent.args.length == alternative.args.length
+            && !consequent.expression.has_side_effects(compressor)
+            && consequent.expression.equivalent_to(alternative.expression)) {
+            if (consequent.args.length == 0) {
+                return make_node(AST_Seq, self, {
+                    car: self.condition,
+                    cdr: consequent
+                });
+            }
+            if (consequent.args.length == 1) {
+                consequent.args[0] = make_node(AST_Conditional, self, {
+                    condition: self.condition,
+                    consequent: consequent.args[0],
+                    alternative: alternative.args[0]
+                });
+                return consequent;
+            }
+        }
+        // x?y?z:a:a --> x&&y?z:a
+        if (consequent instanceof AST_Conditional
+            && consequent.alternative.equivalent_to(alternative)) {
+            return make_node(AST_Conditional, self, {
+                condition: make_node(AST_Binary, self, {
+                    left: self.condition,
+                    operator: "&&",
+                    right: consequent.condition
+                }),
+                consequent: consequent.consequent,
+                alternative: alternative
+            });
+        }
+        // x=y?1:1 --> x=1
+        if (consequent instanceof AST_Constant
+            && alternative instanceof AST_Constant
+            && consequent.equivalent_to(alternative)) {
+            if (self.condition.has_side_effects(compressor)) {
+                return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent.value, self)]);
+            } else {
+                return make_node_from_constant(compressor, consequent.value, self);
+
+            }
+        }
+        // x=y?true:false --> x=!!y
+        if (consequent instanceof AST_True
+            && alternative instanceof AST_False) {
+            self.condition = self.condition.negate(compressor);
+            return make_node(AST_UnaryPrefix, self.condition, {
+                operator: "!",
+                expression: self.condition
+            });
+        }
+        // x=y?false:true --> x=!y
+        if (consequent instanceof AST_False
+            && alternative instanceof AST_True) {
+            return self.condition.negate(compressor)
+        }
+        return self;
+    });
+
+    OPT(AST_Boolean, function(self, compressor){
+        if (compressor.option("booleans")) {
+            var p = compressor.parent();
+            if (p instanceof AST_Binary && (p.operator == "=="
+                                            || p.operator == "!=")) {
+                compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
+                    operator : p.operator,
+                    value    : self.value,
+                    file     : p.start.file,
+                    line     : p.start.line,
+                    col      : p.start.col,
+                });
+                return make_node(AST_Number, self, {
+                    value: +self.value
+                });
+            }
+            return make_node(AST_UnaryPrefix, self, {
+                operator: "!",
+                expression: make_node(AST_Number, self, {
+                    value: 1 - self.value
+                })
+            });
+        }
+        return self;
+    });
+
+    OPT(AST_Sub, function(self, compressor){
+        var prop = self.property;
+        if (prop instanceof AST_String && compressor.option("properties")) {
+            prop = prop.getValue();
+            if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
+                return make_node(AST_Dot, self, {
+                    expression : self.expression,
+                    property   : prop
+                }).optimize(compressor);
+            }
+            var v = parseFloat(prop);
+            if (!isNaN(v) && v.toString() == prop) {
+                self.property = make_node(AST_Number, self.property, {
+                    value: v
+                });
+            }
+        }
+        return self;
+    });
+
+    OPT(AST_Dot, function(self, compressor){
+        var prop = self.property;
+        if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
+            return make_node(AST_Sub, self, {
+                expression : self.expression,
+                property   : make_node(AST_String, self, {
+                    value: prop
+                })
+            }).optimize(compressor);
+        }
+        return self.evaluate(compressor)[0];
+    });
+
+    function literals_in_boolean_context(self, compressor) {
+        if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) {
+            return make_node(AST_True, self);
+        }
+        return self;
+    };
+    OPT(AST_Array, literals_in_boolean_context);
+    OPT(AST_Object, literals_in_boolean_context);
+    OPT(AST_RegExp, literals_in_boolean_context);
+
+})();
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/mozilla-ast.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/mozilla-ast.js
new file mode 100644
index 0000000..1deb18e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/mozilla-ast.js
@@ -0,0 +1,507 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+(function(){
+
+    var MOZ_TO_ME = {
+        ExpressionStatement: function(M) {
+            var expr = M.expression;
+            if (expr.type === "Literal" && typeof expr.value === "string") {
+                return new AST_Directive({
+                    start: my_start_token(M),
+                    end: my_end_token(M),
+                    value: expr.value
+                });
+            }
+            return new AST_SimpleStatement({
+                start: my_start_token(M),
+                end: my_end_token(M),
+                body: from_moz(expr)
+            });
+        },
+        TryStatement: function(M) {
+            var handlers = M.handlers || [M.handler];
+            if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) {
+                throw new Error("Multiple catch clauses are not supported.");
+            }
+            return new AST_Try({
+                start    : my_start_token(M),
+                end      : my_end_token(M),
+                body     : from_moz(M.block).body,
+                bcatch   : from_moz(handlers[0]),
+                bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
+            });
+        },
+        Property: function(M) {
+            var key = M.key;
+            var name = key.type == "Identifier" ? key.name : key.value;
+            var args = {
+                start    : my_start_token(key),
+                end      : my_end_token(M.value),
+                key      : name,
+                value    : from_moz(M.value)
+            };
+            switch (M.kind) {
+              case "init":
+                return new AST_ObjectKeyVal(args);
+              case "set":
+                args.value.name = from_moz(key);
+                return new AST_ObjectSetter(args);
+              case "get":
+                args.value.name = from_moz(key);
+                return new AST_ObjectGetter(args);
+            }
+        },
+        ObjectExpression: function(M) {
+            return new AST_Object({
+                start      : my_start_token(M),
+                end        : my_end_token(M),
+                properties : M.properties.map(function(prop){
+                    prop.type = "Property";
+                    return from_moz(prop)
+                })
+            });
+        },
+        SequenceExpression: function(M) {
+            return AST_Seq.from_array(M.expressions.map(from_moz));
+        },
+        MemberExpression: function(M) {
+            return new (M.computed ? AST_Sub : AST_Dot)({
+                start      : my_start_token(M),
+                end        : my_end_token(M),
+                property   : M.computed ? from_moz(M.property) : M.property.name,
+                expression : from_moz(M.object)
+            });
+        },
+        SwitchCase: function(M) {
+            return new (M.test ? AST_Case : AST_Default)({
+                start      : my_start_token(M),
+                end        : my_end_token(M),
+                expression : from_moz(M.test),
+                body       : M.consequent.map(from_moz)
+            });
+        },
+        VariableDeclaration: function(M) {
+            return new (M.kind === "const" ? AST_Const : AST_Var)({
+                start       : my_start_token(M),
+                end         : my_end_token(M),
+                definitions : M.declarations.map(from_moz)
+            });
+        },
+        Literal: function(M) {
+            var val = M.value, args = {
+                start  : my_start_token(M),
+                end    : my_end_token(M)
+            };
+            if (val === null) return new AST_Null(args);
+            switch (typeof val) {
+              case "string":
+                args.value = val;
+                return new AST_String(args);
+              case "number":
+                args.value = val;
+                return new AST_Number(args);
+              case "boolean":
+                return new (val ? AST_True : AST_False)(args);
+              default:
+                args.value = val;
+                return new AST_RegExp(args);
+            }
+        },
+        Identifier: function(M) {
+            var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
+            return new (  p.type == "LabeledStatement" ? AST_Label
+                        : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
+                        : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
+                        : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
+                        : p.type == "CatchClause" ? AST_SymbolCatch
+                        : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef
+                        : AST_SymbolRef)({
+                            start : my_start_token(M),
+                            end   : my_end_token(M),
+                            name  : M.name
+                        });
+        }
+    };
+
+    MOZ_TO_ME.UpdateExpression =
+    MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) {
+        var prefix = "prefix" in M ? M.prefix
+            : M.type == "UnaryExpression" ? true : false;
+        return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
+            start      : my_start_token(M),
+            end        : my_end_token(M),
+            operator   : M.operator,
+            expression : from_moz(M.argument)
+        });
+    };
+
+    map("Program", AST_Toplevel, "body@body");
+    map("EmptyStatement", AST_EmptyStatement);
+    map("BlockStatement", AST_BlockStatement, "body@body");
+    map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
+    map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
+    map("BreakStatement", AST_Break, "label>label");
+    map("ContinueStatement", AST_Continue, "label>label");
+    map("WithStatement", AST_With, "object>expression, body>body");
+    map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body");
+    map("ReturnStatement", AST_Return, "argument>value");
+    map("ThrowStatement", AST_Throw, "argument>value");
+    map("WhileStatement", AST_While, "test>condition, body>body");
+    map("DoWhileStatement", AST_Do, "test>condition, body>body");
+    map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
+    map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
+    map("DebuggerStatement", AST_Debugger);
+    map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
+    map("VariableDeclarator", AST_VarDef, "id>name, init>value");
+    map("CatchClause", AST_Catch, "param>argname, body%body");
+
+    map("ThisExpression", AST_This);
+    map("ArrayExpression", AST_Array, "elements@elements");
+    map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
+    map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
+    map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
+    map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
+    map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
+    map("NewExpression", AST_New, "callee>expression, arguments@args");
+    map("CallExpression", AST_Call, "callee>expression, arguments@args");
+
+    def_to_moz(AST_Directive, function To_Moz_Directive(M) {
+        return {
+            type: "ExpressionStatement",
+            expression: {
+                type: "Literal",
+                value: M.value
+            }
+        };
+    });
+
+    def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
+        return {
+            type: "ExpressionStatement",
+            expression: to_moz(M.body)
+        };
+    });
+
+    def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) {
+        return {
+            type: "SwitchCase",
+            test: to_moz(M.expression),
+            consequent: M.body.map(to_moz)
+        };
+    });
+
+    def_to_moz(AST_Try, function To_Moz_TryStatement(M) {
+        return {
+            type: "TryStatement",
+            block: to_moz_block(M),
+            handler: to_moz(M.bcatch),
+            guardedHandlers: [],
+            finalizer: to_moz(M.bfinally)
+        };
+    });
+
+    def_to_moz(AST_Catch, function To_Moz_CatchClause(M) {
+        return {
+            type: "CatchClause",
+            param: to_moz(M.argname),
+            guard: null,
+            body: to_moz_block(M)
+        };
+    });
+
+    def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
+        return {
+            type: "VariableDeclaration",
+            kind: M instanceof AST_Const ? "const" : "var",
+            declarations: M.definitions.map(to_moz)
+        };
+    });
+
+    def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
+        return {
+            type: "SequenceExpression",
+            expressions: M.to_array().map(to_moz)
+        };
+    });
+
+    def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
+        var isComputed = M instanceof AST_Sub;
+        return {
+            type: "MemberExpression",
+            object: to_moz(M.expression),
+            computed: isComputed,
+            property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
+        };
+    });
+
+    def_to_moz(AST_Unary, function To_Moz_Unary(M) {
+        return {
+            type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression",
+            operator: M.operator,
+            prefix: M instanceof AST_UnaryPrefix,
+            argument: to_moz(M.expression)
+        };
+    });
+
+    def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) {
+        return {
+            type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression",
+            left: to_moz(M.left),
+            operator: M.operator,
+            right: to_moz(M.right)
+        };
+    });
+
+    def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
+        return {
+            type: "ObjectExpression",
+            properties: M.properties.map(to_moz)
+        };
+    });
+
+    def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
+        var key = (
+            is_identifier(M.key)
+            ? {type: "Identifier", name: M.key}
+            : {type: "Literal", value: M.key}
+        );
+        var kind;
+        if (M instanceof AST_ObjectKeyVal) {
+            kind = "init";
+        } else
+        if (M instanceof AST_ObjectGetter) {
+            kind = "get";
+        } else
+        if (M instanceof AST_ObjectSetter) {
+            kind = "set";
+        }
+        return {
+            type: "Property",
+            kind: kind,
+            key: key,
+            value: to_moz(M.value)
+        };
+    });
+
+    def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
+        var def = M.definition();
+        return {
+            type: "Identifier",
+            name: def ? def.mangled_name || def.name : M.name
+        };
+    });
+
+    def_to_moz(AST_Constant, function To_Moz_Literal(M) {
+        var value = M.value;
+        if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
+            return {
+                type: "UnaryExpression",
+                operator: "-",
+                prefix: true,
+                argument: {
+                    type: "Literal",
+                    value: -value
+                }
+            };
+        }
+        return {
+            type: "Literal",
+            value: value
+        };
+    });
+
+    def_to_moz(AST_Atom, function To_Moz_Atom(M) {
+        return {
+            type: "Identifier",
+            name: String(M.value)
+        };
+    });
+
+    AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
+    AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
+    AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
+
+    AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
+    AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
+
+    /* -----[ tools ]----- */
+
+    function my_start_token(moznode) {
+        var loc = moznode.loc, start = loc && loc.start;
+        var range = moznode.range;
+        return new AST_Token({
+            file    : loc && loc.source,
+            line    : start && start.line,
+            col     : start && start.column,
+            pos     : range ? range[0] : moznode.start,
+            endline : start && start.line,
+            endcol  : start && start.column,
+            endpos  : range ? range[0] : moznode.start
+        });
+    };
+
+    function my_end_token(moznode) {
+        var loc = moznode.loc, end = loc && loc.end;
+        var range = moznode.range;
+        return new AST_Token({
+            file    : loc && loc.source,
+            line    : end && end.line,
+            col     : end && end.column,
+            pos     : range ? range[1] : moznode.end,
+            endline : end && end.line,
+            endcol  : end && end.column,
+            endpos  : range ? range[1] : moznode.end
+        });
+    };
+
+    function map(moztype, mytype, propmap) {
+        var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
+        moz_to_me += "return new " + mytype.name + "({\n" +
+            "start: my_start_token(M),\n" +
+            "end: my_end_token(M)";
+
+        var me_to_moz = "function To_Moz_" + moztype + "(M){\n";
+        me_to_moz += "return {\n" +
+            "type: " + JSON.stringify(moztype);
+
+        if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
+            var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
+            if (!m) throw new Error("Can't understand property map: " + prop);
+            var moz = m[1], how = m[2], my = m[3];
+            moz_to_me += ",\n" + my + ": ";
+            me_to_moz += ",\n" + moz + ": ";
+            switch (how) {
+                case "@":
+                    moz_to_me += "M." + moz + ".map(from_moz)";
+                    me_to_moz += "M." +  my + ".map(to_moz)";
+                    break;
+                case ">":
+                    moz_to_me += "from_moz(M." + moz + ")";
+                    me_to_moz += "to_moz(M." + my + ")";
+                    break;
+                case "=":
+                    moz_to_me += "M." + moz;
+                    me_to_moz += "M." + my;
+                    break;
+                case "%":
+                    moz_to_me += "from_moz(M." + moz + ").body";
+                    me_to_moz += "to_moz_block(M)";
+                    break;
+                default:
+                    throw new Error("Can't understand operator in propmap: " + prop);
+            }
+        });
+
+        moz_to_me += "\n})\n}";
+        me_to_moz += "\n}\n}";
+
+        //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
+        //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
+        //console.log(moz_to_me);
+
+        moz_to_me = new Function("my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
+            my_start_token, my_end_token, from_moz
+        );
+        me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
+            to_moz, to_moz_block
+        );
+        MOZ_TO_ME[moztype] = moz_to_me;
+        def_to_moz(mytype, me_to_moz);
+    };
+
+    var FROM_MOZ_STACK = null;
+
+    function from_moz(node) {
+        FROM_MOZ_STACK.push(node);
+        var ret = node != null ? MOZ_TO_ME[node.type](node) : null;
+        FROM_MOZ_STACK.pop();
+        return ret;
+    };
+
+    AST_Node.from_mozilla_ast = function(node){
+        var save_stack = FROM_MOZ_STACK;
+        FROM_MOZ_STACK = [];
+        var ast = from_moz(node);
+        FROM_MOZ_STACK = save_stack;
+        return ast;
+    };
+
+    function set_moz_loc(mynode, moznode, myparent) {
+        var start = mynode.start;
+        var end = mynode.end;
+        if (start.pos != null && end.endpos != null) {
+            moznode.range = [start.pos, end.endpos];
+        }
+        if (start.line) {
+            moznode.loc = {
+                start: {line: start.line, column: start.col},
+                end: end.endline ? {line: end.endline, column: end.endcol} : null
+            };
+            if (start.file) {
+                moznode.loc.source = start.file;
+            }
+        }
+        return moznode;
+    };
+
+    function def_to_moz(mytype, handler) {
+        mytype.DEFMETHOD("to_mozilla_ast", function() {
+            return set_moz_loc(this, handler(this));
+        });
+    };
+
+    function to_moz(node) {
+        return node != null ? node.to_mozilla_ast() : null;
+    };
+
+    function to_moz_block(node) {
+        return {
+            type: "BlockStatement",
+            body: node.body.map(to_moz)
+        };
+    };
+
+})();
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/output.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/output.js
new file mode 100644
index 0000000..1d67b1b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/output.js
@@ -0,0 +1,1326 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+function OutputStream(options) {
+
+    options = defaults(options, {
+        indent_start     : 0,
+        indent_level     : 4,
+        quote_keys       : false,
+        space_colon      : true,
+        ascii_only       : false,
+        unescape_regexps : false,
+        inline_script    : false,
+        width            : 80,
+        max_line_len     : 32000,
+        beautify         : false,
+        source_map       : null,
+        bracketize       : false,
+        semicolons       : true,
+        comments         : false,
+        preserve_line    : false,
+        screw_ie8        : false,
+        preamble         : null,
+        quote_style      : 0
+    }, true);
+
+    var indentation = 0;
+    var current_col = 0;
+    var current_line = 1;
+    var current_pos = 0;
+    var OUTPUT = "";
+
+    function to_ascii(str, identifier) {
+        return str.replace(/[\u0080-\uffff]/g, function(ch) {
+            var code = ch.charCodeAt(0).toString(16);
+            if (code.length <= 2 && !identifier) {
+                while (code.length < 2) code = "0" + code;
+                return "\\x" + code;
+            } else {
+                while (code.length < 4) code = "0" + code;
+                return "\\u" + code;
+            }
+        });
+    };
+
+    function make_string(str, quote) {
+        var dq = 0, sq = 0;
+        str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){
+            switch (s) {
+              case "\\": return "\\\\";
+              case "\b": return "\\b";
+              case "\f": return "\\f";
+              case "\n": return "\\n";
+              case "\r": return "\\r";
+              case "\u2028": return "\\u2028";
+              case "\u2029": return "\\u2029";
+              case '"': ++dq; return '"';
+              case "'": ++sq; return "'";
+              case "\0": return "\\x00";
+              case "\ufeff": return "\\ufeff";
+            }
+            return s;
+        });
+        function quote_single() {
+            return "'" + str.replace(/\x27/g, "\\'") + "'";
+        }
+        function quote_double() {
+            return '"' + str.replace(/\x22/g, '\\"') + '"';
+        }
+        if (options.ascii_only) str = to_ascii(str);
+        switch (options.quote_style) {
+          case 1:
+            return quote_single();
+          case 2:
+            return quote_double();
+          case 3:
+            return quote == "'" ? quote_single() : quote_double();
+          default:
+            return dq > sq ? quote_single() : quote_double();
+        }
+    };
+
+    function encode_string(str, quote) {
+        var ret = make_string(str, quote);
+        if (options.inline_script)
+            ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
+        return ret;
+    };
+
+    function make_name(name) {
+        name = name.toString();
+        if (options.ascii_only)
+            name = to_ascii(name, true);
+        return name;
+    };
+
+    function make_indent(back) {
+        return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
+    };
+
+    /* -----[ beautification/minification ]----- */
+
+    var might_need_space = false;
+    var might_need_semicolon = false;
+    var last = null;
+
+    function last_char() {
+        return last.charAt(last.length - 1);
+    };
+
+    function maybe_newline() {
+        if (options.max_line_len && current_col > options.max_line_len)
+            print("\n");
+    };
+
+    var requireSemicolonChars = makePredicate("( [ + * / - , .");
+
+    function print(str) {
+        str = String(str);
+        var ch = str.charAt(0);
+        if (might_need_semicolon) {
+            if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
+                if (options.semicolons || requireSemicolonChars(ch)) {
+                    OUTPUT += ";";
+                    current_col++;
+                    current_pos++;
+                } else {
+                    OUTPUT += "\n";
+                    current_pos++;
+                    current_line++;
+                    current_col = 0;
+                }
+                if (!options.beautify)
+                    might_need_space = false;
+            }
+            might_need_semicolon = false;
+        }
+
+        if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
+            var target_line = stack[stack.length - 1].start.line;
+            while (current_line < target_line) {
+                OUTPUT += "\n";
+                current_pos++;
+                current_line++;
+                current_col = 0;
+                might_need_space = false;
+            }
+        }
+
+        if (might_need_space) {
+            var prev = last_char();
+            if ((is_identifier_char(prev)
+                 && (is_identifier_char(ch) || ch == "\\"))
+                || (/^[\+\-\/]$/.test(ch) && ch == prev))
+            {
+                OUTPUT += " ";
+                current_col++;
+                current_pos++;
+            }
+            might_need_space = false;
+        }
+        var a = str.split(/\r?\n/), n = a.length - 1;
+        current_line += n;
+        if (n == 0) {
+            current_col += a[n].length;
+        } else {
+            current_col = a[n].length;
+        }
+        current_pos += str.length;
+        last = str;
+        OUTPUT += str;
+    };
+
+    var space = options.beautify ? function() {
+        print(" ");
+    } : function() {
+        might_need_space = true;
+    };
+
+    var indent = options.beautify ? function(half) {
+        if (options.beautify) {
+            print(make_indent(half ? 0.5 : 0));
+        }
+    } : noop;
+
+    var with_indent = options.beautify ? function(col, cont) {
+        if (col === true) col = next_indent();
+        var save_indentation = indentation;
+        indentation = col;
+        var ret = cont();
+        indentation = save_indentation;
+        return ret;
+    } : function(col, cont) { return cont() };
+
+    var newline = options.beautify ? function() {
+        print("\n");
+    } : maybe_newline;
+
+    var semicolon = options.beautify ? function() {
+        print(";");
+    } : function() {
+        might_need_semicolon = true;
+    };
+
+    function force_semicolon() {
+        might_need_semicolon = false;
+        print(";");
+    };
+
+    function next_indent() {
+        return indentation + options.indent_level;
+    };
+
+    function with_block(cont) {
+        var ret;
+        print("{");
+        newline();
+        with_indent(next_indent(), function(){
+            ret = cont();
+        });
+        indent();
+        print("}");
+        return ret;
+    };
+
+    function with_parens(cont) {
+        print("(");
+        //XXX: still nice to have that for argument lists
+        //var ret = with_indent(current_col, cont);
+        var ret = cont();
+        print(")");
+        return ret;
+    };
+
+    function with_square(cont) {
+        print("[");
+        //var ret = with_indent(current_col, cont);
+        var ret = cont();
+        print("]");
+        return ret;
+    };
+
+    function comma() {
+        print(",");
+        space();
+    };
+
+    function colon() {
+        print(":");
+        if (options.space_colon) space();
+    };
+
+    var add_mapping = options.source_map ? function(token, name) {
+        try {
+            if (token) options.source_map.add(
+                token.file || "?",
+                current_line, current_col,
+                token.line, token.col,
+                (!name && token.type == "name") ? token.value : name
+            );
+        } catch(ex) {
+            AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
+                file: token.file,
+                line: token.line,
+                col: token.col,
+                cline: current_line,
+                ccol: current_col,
+                name: name || ""
+            })
+        }
+    } : noop;
+
+    function get() {
+        return OUTPUT;
+    };
+
+    if (options.preamble) {
+        print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
+    }
+
+    var stack = [];
+    return {
+        get             : get,
+        toString        : get,
+        indent          : indent,
+        indentation     : function() { return indentation },
+        current_width   : function() { return current_col - indentation },
+        should_break    : function() { return options.width && this.current_width() >= options.width },
+        newline         : newline,
+        print           : print,
+        space           : space,
+        comma           : comma,
+        colon           : colon,
+        last            : function() { return last },
+        semicolon       : semicolon,
+        force_semicolon : force_semicolon,
+        to_ascii        : to_ascii,
+        print_name      : function(name) { print(make_name(name)) },
+        print_string    : function(str, quote) { print(encode_string(str, quote)) },
+        next_indent     : next_indent,
+        with_indent     : with_indent,
+        with_block      : with_block,
+        with_parens     : with_parens,
+        with_square     : with_square,
+        add_mapping     : add_mapping,
+        option          : function(opt) { return options[opt] },
+        line            : function() { return current_line },
+        col             : function() { return current_col },
+        pos             : function() { return current_pos },
+        push_node       : function(node) { stack.push(node) },
+        pop_node        : function() { return stack.pop() },
+        stack           : function() { return stack },
+        parent          : function(n) {
+            return stack[stack.length - 2 - (n || 0)];
+        }
+    };
+
+};
+
+/* -----[ code generators ]----- */
+
+(function(){
+
+    /* -----[ utils ]----- */
+
+    function DEFPRINT(nodetype, generator) {
+        nodetype.DEFMETHOD("_codegen", generator);
+    };
+
+    AST_Node.DEFMETHOD("print", function(stream, force_parens){
+        var self = this, generator = self._codegen;
+        function doit() {
+            self.add_comments(stream);
+            self.add_source_map(stream);
+            generator(self, stream);
+        }
+        stream.push_node(self);
+        if (force_parens || self.needs_parens(stream)) {
+            stream.with_parens(doit);
+        } else {
+            doit();
+        }
+        stream.pop_node();
+    });
+
+    AST_Node.DEFMETHOD("print_to_string", function(options){
+        var s = OutputStream(options);
+        this.print(s);
+        return s.get();
+    });
+
+    /* -----[ comments ]----- */
+
+    AST_Node.DEFMETHOD("add_comments", function(output){
+        var c = output.option("comments"), self = this;
+        if (c) {
+            var start = self.start;
+            if (start && !start._comments_dumped) {
+                start._comments_dumped = true;
+                var comments = start.comments_before || [];
+
+                // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
+                //               and https://github.com/mishoo/UglifyJS2/issues/372
+                if (self instanceof AST_Exit && self.value) {
+                    self.value.walk(new TreeWalker(function(node){
+                        if (node.start && node.start.comments_before) {
+                            comments = comments.concat(node.start.comments_before);
+                            node.start.comments_before = [];
+                        }
+                        if (node instanceof AST_Function ||
+                            node instanceof AST_Array ||
+                            node instanceof AST_Object)
+                        {
+                            return true; // don't go inside.
+                        }
+                    }));
+                }
+
+                if (c.test) {
+                    comments = comments.filter(function(comment){
+                        return c.test(comment.value);
+                    });
+                } else if (typeof c == "function") {
+                    comments = comments.filter(function(comment){
+                        return c(self, comment);
+                    });
+                }
+
+                // Keep single line comments after nlb, after nlb
+                if (!output.option("beautify") && comments.length > 0 &&
+                    /comment[134]/.test(comments[0].type) &&
+                    output.col() !== 0 && comments[0].nlb)
+                {
+                    output.print("\n");
+                }
+
+                comments.forEach(function(c){
+                    if (/comment[134]/.test(c.type)) {
+                        output.print("//" + c.value + "\n");
+                        output.indent();
+                    }
+                    else if (c.type == "comment2") {
+                        output.print("/*" + c.value + "*/");
+                        if (start.nlb) {
+                            output.print("\n");
+                            output.indent();
+                        } else {
+                            output.space();
+                        }
+                    }
+                });
+            }
+        }
+    });
+
+    /* -----[ PARENTHESES ]----- */
+
+    function PARENS(nodetype, func) {
+        if (Array.isArray(nodetype)) {
+            nodetype.forEach(function(nodetype){
+                PARENS(nodetype, func);
+            });
+        } else {
+            nodetype.DEFMETHOD("needs_parens", func);
+        }
+    };
+
+    PARENS(AST_Node, function(){
+        return false;
+    });
+
+    // a function expression needs parens around it when it's provably
+    // the first token to appear in a statement.
+    PARENS(AST_Function, function(output){
+        return first_in_statement(output);
+    });
+
+    // same goes for an object literal, because otherwise it would be
+    // interpreted as a block of code.
+    PARENS(AST_Object, function(output){
+        return first_in_statement(output);
+    });
+
+    PARENS([ AST_Unary, AST_Undefined ], function(output){
+        var p = output.parent();
+        return p instanceof AST_PropAccess && p.expression === this;
+    });
+
+    PARENS(AST_Seq, function(output){
+        var p = output.parent();
+        return p instanceof AST_Call             // (foo, bar)() or foo(1, (2, 3), 4)
+            || p instanceof AST_Unary            // !(foo, bar, baz)
+            || p instanceof AST_Binary           // 1 + (2, 3) + 4 ==> 8
+            || p instanceof AST_VarDef           // var a = (1, 2), b = a + a; ==> b == 4
+            || p instanceof AST_PropAccess       // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
+            || p instanceof AST_Array            // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
+            || p instanceof AST_ObjectProperty   // { foo: (1, 2) }.foo ==> 2
+            || p instanceof AST_Conditional      /* (false, true) ? (a = 10, b = 20) : (c = 30)
+                                                  * ==> 20 (side effect, set a := 10 and b := 20) */
+        ;
+    });
+
+    PARENS(AST_Binary, function(output){
+        var p = output.parent();
+        // (foo && bar)()
+        if (p instanceof AST_Call && p.expression === this)
+            return true;
+        // typeof (foo && bar)
+        if (p instanceof AST_Unary)
+            return true;
+        // (foo && bar)["prop"], (foo && bar).prop
+        if (p instanceof AST_PropAccess && p.expression === this)
+            return true;
+        // this deals with precedence: 3 * (2 + 1)
+        if (p instanceof AST_Binary) {
+            var po = p.operator, pp = PRECEDENCE[po];
+            var so = this.operator, sp = PRECEDENCE[so];
+            if (pp > sp
+                || (pp == sp
+                    && this === p.right)) {
+                return true;
+            }
+        }
+    });
+
+    PARENS(AST_PropAccess, function(output){
+        var p = output.parent();
+        if (p instanceof AST_New && p.expression === this) {
+            // i.e. new (foo.bar().baz)
+            //
+            // if there's one call into this subtree, then we need
+            // parens around it too, otherwise the call will be
+            // interpreted as passing the arguments to the upper New
+            // expression.
+            try {
+                this.walk(new TreeWalker(function(node){
+                    if (node instanceof AST_Call) throw p;
+                }));
+            } catch(ex) {
+                if (ex !== p) throw ex;
+                return true;
+            }
+        }
+    });
+
+    PARENS(AST_Call, function(output){
+        var p = output.parent(), p1;
+        if (p instanceof AST_New && p.expression === this)
+            return true;
+
+        // workaround for Safari bug.
+        // https://bugs.webkit.org/show_bug.cgi?id=123506
+        return this.expression instanceof AST_Function
+            && p instanceof AST_PropAccess
+            && p.expression === this
+            && (p1 = output.parent(1)) instanceof AST_Assign
+            && p1.left === p;
+    });
+
+    PARENS(AST_New, function(output){
+        var p = output.parent();
+        if (no_constructor_parens(this, output)
+            && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
+                || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
+            return true;
+    });
+
+    PARENS(AST_Number, function(output){
+        var p = output.parent();
+        if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
+            return true;
+    });
+
+    PARENS([ AST_Assign, AST_Conditional ], function (output){
+        var p = output.parent();
+        // !(a = false) → true
+        if (p instanceof AST_Unary)
+            return true;
+        // 1 + (a = 2) + 3 → 6, side effect setting a = 2
+        if (p instanceof AST_Binary && !(p instanceof AST_Assign))
+            return true;
+        // (a = func)() —or— new (a = Object)()
+        if (p instanceof AST_Call && p.expression === this)
+            return true;
+        // (a = foo) ? bar : baz
+        if (p instanceof AST_Conditional && p.condition === this)
+            return true;
+        // (a = foo)["prop"] —or— (a = foo).prop
+        if (p instanceof AST_PropAccess && p.expression === this)
+            return true;
+    });
+
+    /* -----[ PRINTERS ]----- */
+
+    DEFPRINT(AST_Directive, function(self, output){
+        output.print_string(self.value, self.quote);
+        output.semicolon();
+    });
+    DEFPRINT(AST_Debugger, function(self, output){
+        output.print("debugger");
+        output.semicolon();
+    });
+
+    /* -----[ statements ]----- */
+
+    function display_body(body, is_toplevel, output) {
+        var last = body.length - 1;
+        body.forEach(function(stmt, i){
+            if (!(stmt instanceof AST_EmptyStatement)) {
+                output.indent();
+                stmt.print(output);
+                if (!(i == last && is_toplevel)) {
+                    output.newline();
+                    if (is_toplevel) output.newline();
+                }
+            }
+        });
+    };
+
+    AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
+        force_statement(this.body, output);
+    });
+
+    DEFPRINT(AST_Statement, function(self, output){
+        self.body.print(output);
+        output.semicolon();
+    });
+    DEFPRINT(AST_Toplevel, function(self, output){
+        display_body(self.body, true, output);
+        output.print("");
+    });
+    DEFPRINT(AST_LabeledStatement, function(self, output){
+        self.label.print(output);
+        output.colon();
+        self.body.print(output);
+    });
+    DEFPRINT(AST_SimpleStatement, function(self, output){
+        self.body.print(output);
+        output.semicolon();
+    });
+    function print_bracketed(body, output) {
+        if (body.length > 0) output.with_block(function(){
+            display_body(body, false, output);
+        });
+        else output.print("{}");
+    };
+    DEFPRINT(AST_BlockStatement, function(self, output){
+        print_bracketed(self.body, output);
+    });
+    DEFPRINT(AST_EmptyStatement, function(self, output){
+        output.semicolon();
+    });
+    DEFPRINT(AST_Do, function(self, output){
+        output.print("do");
+        output.space();
+        self._do_print_body(output);
+        output.space();
+        output.print("while");
+        output.space();
+        output.with_parens(function(){
+            self.condition.print(output);
+        });
+        output.semicolon();
+    });
+    DEFPRINT(AST_While, function(self, output){
+        output.print("while");
+        output.space();
+        output.with_parens(function(){
+            self.condition.print(output);
+        });
+        output.space();
+        self._do_print_body(output);
+    });
+    DEFPRINT(AST_For, function(self, output){
+        output.print("for");
+        output.space();
+        output.with_parens(function(){
+            if (self.init && !(self.init instanceof AST_EmptyStatement)) {
+                if (self.init instanceof AST_Definitions) {
+                    self.init.print(output);
+                } else {
+                    parenthesize_for_noin(self.init, output, true);
+                }
+                output.print(";");
+                output.space();
+            } else {
+                output.print(";");
+            }
+            if (self.condition) {
+                self.condition.print(output);
+                output.print(";");
+                output.space();
+            } else {
+                output.print(";");
+            }
+            if (self.step) {
+                self.step.print(output);
+            }
+        });
+        output.space();
+        self._do_print_body(output);
+    });
+    DEFPRINT(AST_ForIn, function(self, output){
+        output.print("for");
+        output.space();
+        output.with_parens(function(){
+            self.init.print(output);
+            output.space();
+            output.print("in");
+            output.space();
+            self.object.print(output);
+        });
+        output.space();
+        self._do_print_body(output);
+    });
+    DEFPRINT(AST_With, function(self, output){
+        output.print("with");
+        output.space();
+        output.with_parens(function(){
+            self.expression.print(output);
+        });
+        output.space();
+        self._do_print_body(output);
+    });
+
+    /* -----[ functions ]----- */
+    AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
+        var self = this;
+        if (!nokeyword) {
+            output.print("function");
+        }
+        if (self.name) {
+            output.space();
+            self.name.print(output);
+        }
+        output.with_parens(function(){
+            self.argnames.forEach(function(arg, i){
+                if (i) output.comma();
+                arg.print(output);
+            });
+        });
+        output.space();
+        print_bracketed(self.body, output);
+    });
+    DEFPRINT(AST_Lambda, function(self, output){
+        self._do_print(output);
+    });
+
+    /* -----[ exits ]----- */
+    AST_Exit.DEFMETHOD("_do_print", function(output, kind){
+        output.print(kind);
+        if (this.value) {
+            output.space();
+            this.value.print(output);
+        }
+        output.semicolon();
+    });
+    DEFPRINT(AST_Return, function(self, output){
+        self._do_print(output, "return");
+    });
+    DEFPRINT(AST_Throw, function(self, output){
+        self._do_print(output, "throw");
+    });
+
+    /* -----[ loop control ]----- */
+    AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
+        output.print(kind);
+        if (this.label) {
+            output.space();
+            this.label.print(output);
+        }
+        output.semicolon();
+    });
+    DEFPRINT(AST_Break, function(self, output){
+        self._do_print(output, "break");
+    });
+    DEFPRINT(AST_Continue, function(self, output){
+        self._do_print(output, "continue");
+    });
+
+    /* -----[ if ]----- */
+    function make_then(self, output) {
+        if (output.option("bracketize")) {
+            make_block(self.body, output);
+            return;
+        }
+        // The squeezer replaces "block"-s that contain only a single
+        // statement with the statement itself; technically, the AST
+        // is correct, but this can create problems when we output an
+        // IF having an ELSE clause where the THEN clause ends in an
+        // IF *without* an ELSE block (then the outer ELSE would refer
+        // to the inner IF).  This function checks for this case and
+        // adds the block brackets if needed.
+        if (!self.body)
+            return output.force_semicolon();
+        if (self.body instanceof AST_Do
+            && !output.option("screw_ie8")) {
+            // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
+            // croaks with "syntax error" on code like this: if (foo)
+            // do ... while(cond); else ...  we need block brackets
+            // around do/while
+            make_block(self.body, output);
+            return;
+        }
+        var b = self.body;
+        while (true) {
+            if (b instanceof AST_If) {
+                if (!b.alternative) {
+                    make_block(self.body, output);
+                    return;
+                }
+                b = b.alternative;
+            }
+            else if (b instanceof AST_StatementWithBody) {
+                b = b.body;
+            }
+            else break;
+        }
+        force_statement(self.body, output);
+    };
+    DEFPRINT(AST_If, function(self, output){
+        output.print("if");
+        output.space();
+        output.with_parens(function(){
+            self.condition.print(output);
+        });
+        output.space();
+        if (self.alternative) {
+            make_then(self, output);
+            output.space();
+            output.print("else");
+            output.space();
+            force_statement(self.alternative, output);
+        } else {
+            self._do_print_body(output);
+        }
+    });
+
+    /* -----[ switch ]----- */
+    DEFPRINT(AST_Switch, function(self, output){
+        output.print("switch");
+        output.space();
+        output.with_parens(function(){
+            self.expression.print(output);
+        });
+        output.space();
+        if (self.body.length > 0) output.with_block(function(){
+            self.body.forEach(function(stmt, i){
+                if (i) output.newline();
+                output.indent(true);
+                stmt.print(output);
+            });
+        });
+        else output.print("{}");
+    });
+    AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
+        if (this.body.length > 0) {
+            output.newline();
+            this.body.forEach(function(stmt){
+                output.indent();
+                stmt.print(output);
+                output.newline();
+            });
+        }
+    });
+    DEFPRINT(AST_Default, function(self, output){
+        output.print("default:");
+        self._do_print_body(output);
+    });
+    DEFPRINT(AST_Case, function(self, output){
+        output.print("case");
+        output.space();
+        self.expression.print(output);
+        output.print(":");
+        self._do_print_body(output);
+    });
+
+    /* -----[ exceptions ]----- */
+    DEFPRINT(AST_Try, function(self, output){
+        output.print("try");
+        output.space();
+        print_bracketed(self.body, output);
+        if (self.bcatch) {
+            output.space();
+            self.bcatch.print(output);
+        }
+        if (self.bfinally) {
+            output.space();
+            self.bfinally.print(output);
+        }
+    });
+    DEFPRINT(AST_Catch, function(self, output){
+        output.print("catch");
+        output.space();
+        output.with_parens(function(){
+            self.argname.print(output);
+        });
+        output.space();
+        print_bracketed(self.body, output);
+    });
+    DEFPRINT(AST_Finally, function(self, output){
+        output.print("finally");
+        output.space();
+        print_bracketed(self.body, output);
+    });
+
+    /* -----[ var/const ]----- */
+    AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
+        output.print(kind);
+        output.space();
+        this.definitions.forEach(function(def, i){
+            if (i) output.comma();
+            def.print(output);
+        });
+        var p = output.parent();
+        var in_for = p instanceof AST_For || p instanceof AST_ForIn;
+        var avoid_semicolon = in_for && p.init === this;
+        if (!avoid_semicolon)
+            output.semicolon();
+    });
+    DEFPRINT(AST_Var, function(self, output){
+        self._do_print(output, "var");
+    });
+    DEFPRINT(AST_Const, function(self, output){
+        self._do_print(output, "const");
+    });
+
+    function parenthesize_for_noin(node, output, noin) {
+        if (!noin) node.print(output);
+        else try {
+            // need to take some precautions here:
+            //    https://github.com/mishoo/UglifyJS2/issues/60
+            node.walk(new TreeWalker(function(node){
+                if (node instanceof AST_Binary && node.operator == "in")
+                    throw output;
+            }));
+            node.print(output);
+        } catch(ex) {
+            if (ex !== output) throw ex;
+            node.print(output, true);
+        }
+    };
+
+    DEFPRINT(AST_VarDef, function(self, output){
+        self.name.print(output);
+        if (self.value) {
+            output.space();
+            output.print("=");
+            output.space();
+            var p = output.parent(1);
+            var noin = p instanceof AST_For || p instanceof AST_ForIn;
+            parenthesize_for_noin(self.value, output, noin);
+        }
+    });
+
+    /* -----[ other expressions ]----- */
+    DEFPRINT(AST_Call, function(self, output){
+        self.expression.print(output);
+        if (self instanceof AST_New && no_constructor_parens(self, output))
+            return;
+        output.with_parens(function(){
+            self.args.forEach(function(expr, i){
+                if (i) output.comma();
+                expr.print(output);
+            });
+        });
+    });
+    DEFPRINT(AST_New, function(self, output){
+        output.print("new");
+        output.space();
+        AST_Call.prototype._codegen(self, output);
+    });
+
+    AST_Seq.DEFMETHOD("_do_print", function(output){
+        this.car.print(output);
+        if (this.cdr) {
+            output.comma();
+            if (output.should_break()) {
+                output.newline();
+                output.indent();
+            }
+            this.cdr.print(output);
+        }
+    });
+    DEFPRINT(AST_Seq, function(self, output){
+        self._do_print(output);
+        // var p = output.parent();
+        // if (p instanceof AST_Statement) {
+        //     output.with_indent(output.next_indent(), function(){
+        //         self._do_print(output);
+        //     });
+        // } else {
+        //     self._do_print(output);
+        // }
+    });
+    DEFPRINT(AST_Dot, function(self, output){
+        var expr = self.expression;
+        expr.print(output);
+        if (expr instanceof AST_Number && expr.getValue() >= 0) {
+            if (!/[xa-f.]/i.test(output.last())) {
+                output.print(".");
+            }
+        }
+        output.print(".");
+        // the name after dot would be mapped about here.
+        output.add_mapping(self.end);
+        output.print_name(self.property);
+    });
+    DEFPRINT(AST_Sub, function(self, output){
+        self.expression.print(output);
+        output.print("[");
+        self.property.print(output);
+        output.print("]");
+    });
+    DEFPRINT(AST_UnaryPrefix, function(self, output){
+        var op = self.operator;
+        output.print(op);
+        if (/^[a-z]/i.test(op)
+            || (/[+-]$/.test(op)
+                && self.expression instanceof AST_UnaryPrefix
+                && /^[+-]/.test(self.expression.operator))) {
+            output.space();
+        }
+        self.expression.print(output);
+    });
+    DEFPRINT(AST_UnaryPostfix, function(self, output){
+        self.expression.print(output);
+        output.print(self.operator);
+    });
+    DEFPRINT(AST_Binary, function(self, output){
+        self.left.print(output);
+        output.space();
+        output.print(self.operator);
+        if (self.operator == "<"
+            && self.right instanceof AST_UnaryPrefix
+            && self.right.operator == "!"
+            && self.right.expression instanceof AST_UnaryPrefix
+            && self.right.expression.operator == "--") {
+            // space is mandatory to avoid outputting <!--
+            // http://javascript.spec.whatwg.org/#comment-syntax
+            output.print(" ");
+        } else {
+            // the space is optional depending on "beautify"
+            output.space();
+        }
+        self.right.print(output);
+    });
+    DEFPRINT(AST_Conditional, function(self, output){
+        self.condition.print(output);
+        output.space();
+        output.print("?");
+        output.space();
+        self.consequent.print(output);
+        output.space();
+        output.colon();
+        self.alternative.print(output);
+    });
+
+    /* -----[ literals ]----- */
+    DEFPRINT(AST_Array, function(self, output){
+        output.with_square(function(){
+            var a = self.elements, len = a.length;
+            if (len > 0) output.space();
+            a.forEach(function(exp, i){
+                if (i) output.comma();
+                exp.print(output);
+                // If the final element is a hole, we need to make sure it
+                // doesn't look like a trailing comma, by inserting an actual
+                // trailing comma.
+                if (i === len - 1 && exp instanceof AST_Hole)
+                  output.comma();
+            });
+            if (len > 0) output.space();
+        });
+    });
+    DEFPRINT(AST_Object, function(self, output){
+        if (self.properties.length > 0) output.with_block(function(){
+            self.properties.forEach(function(prop, i){
+                if (i) {
+                    output.print(",");
+                    output.newline();
+                }
+                output.indent();
+                prop.print(output);
+            });
+            output.newline();
+        });
+        else output.print("{}");
+    });
+    DEFPRINT(AST_ObjectKeyVal, function(self, output){
+        var key = self.key;
+        var quote = self.quote;
+        if (output.option("quote_keys")) {
+            output.print_string(key + "");
+        } else if ((typeof key == "number"
+                    || !output.option("beautify")
+                    && +key + "" == key)
+                   && parseFloat(key) >= 0) {
+            output.print(make_num(key));
+        } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
+            output.print_name(key);
+        } else {
+            output.print_string(key, quote);
+        }
+        output.colon();
+        self.value.print(output);
+    });
+    DEFPRINT(AST_ObjectSetter, function(self, output){
+        output.print("set");
+        output.space();
+        self.key.print(output);
+        self.value._do_print(output, true);
+    });
+    DEFPRINT(AST_ObjectGetter, function(self, output){
+        output.print("get");
+        output.space();
+        self.key.print(output);
+        self.value._do_print(output, true);
+    });
+    DEFPRINT(AST_Symbol, function(self, output){
+        var def = self.definition();
+        output.print_name(def ? def.mangled_name || def.name : self.name);
+    });
+    DEFPRINT(AST_Undefined, function(self, output){
+        output.print("void 0");
+    });
+    DEFPRINT(AST_Hole, noop);
+    DEFPRINT(AST_Infinity, function(self, output){
+        output.print("Infinity");
+    });
+    DEFPRINT(AST_NaN, function(self, output){
+        output.print("NaN");
+    });
+    DEFPRINT(AST_This, function(self, output){
+        output.print("this");
+    });
+    DEFPRINT(AST_Constant, function(self, output){
+        output.print(self.getValue());
+    });
+    DEFPRINT(AST_String, function(self, output){
+        output.print_string(self.getValue(), self.quote);
+    });
+    DEFPRINT(AST_Number, function(self, output){
+        output.print(make_num(self.getValue()));
+    });
+
+    function regexp_safe_literal(code) {
+        return [
+            0x5c   , // \
+            0x2f   , // /
+            0x2e   , // .
+            0x2b   , // +
+            0x2a   , // *
+            0x3f   , // ?
+            0x28   , // (
+            0x29   , // )
+            0x5b   , // [
+            0x5d   , // ]
+            0x7b   , // {
+            0x7d   , // }
+            0x24   , // $
+            0x5e   , // ^
+            0x3a   , // :
+            0x7c   , // |
+            0x21   , // !
+            0x0a   , // \n
+            0x0d   , // \r
+            0x00   , // \0
+            0xfeff , // Unicode BOM
+            0x2028 , // unicode "line separator"
+            0x2029 , // unicode "paragraph separator"
+        ].indexOf(code) < 0;
+    };
+
+    DEFPRINT(AST_RegExp, function(self, output){
+        var str = self.getValue().toString();
+        if (output.option("ascii_only")) {
+            str = output.to_ascii(str);
+        } else if (output.option("unescape_regexps")) {
+            str = str.split("\\\\").map(function(str){
+                return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
+                    var code = parseInt(s.substr(2), 16);
+                    return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
+                });
+            }).join("\\\\");
+        }
+        output.print(str);
+        var p = output.parent();
+        if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
+            output.print(" ");
+    });
+
+    function force_statement(stat, output) {
+        if (output.option("bracketize")) {
+            if (!stat || stat instanceof AST_EmptyStatement)
+                output.print("{}");
+            else if (stat instanceof AST_BlockStatement)
+                stat.print(output);
+            else output.with_block(function(){
+                output.indent();
+                stat.print(output);
+                output.newline();
+            });
+        } else {
+            if (!stat || stat instanceof AST_EmptyStatement)
+                output.force_semicolon();
+            else
+                stat.print(output);
+        }
+    };
+
+    // return true if the node at the top of the stack (that means the
+    // innermost node in the current output) is lexically the first in
+    // a statement.
+    function first_in_statement(output) {
+        var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
+        while (i > 0) {
+            if (p instanceof AST_Statement && p.body === node)
+                return true;
+            if ((p instanceof AST_Seq           && p.car === node        ) ||
+                (p instanceof AST_Call          && p.expression === node && !(p instanceof AST_New) ) ||
+                (p instanceof AST_Dot           && p.expression === node ) ||
+                (p instanceof AST_Sub           && p.expression === node ) ||
+                (p instanceof AST_Conditional   && p.condition === node  ) ||
+                (p instanceof AST_Binary        && p.left === node       ) ||
+                (p instanceof AST_UnaryPostfix  && p.expression === node ))
+            {
+                node = p;
+                p = a[--i];
+            } else {
+                return false;
+            }
+        }
+    };
+
+    // self should be AST_New.  decide if we want to show parens or not.
+    function no_constructor_parens(self, output) {
+        return self.args.length == 0 && !output.option("beautify");
+    };
+
+    function best_of(a) {
+        var best = a[0], len = best.length;
+        for (var i = 1; i < a.length; ++i) {
+            if (a[i].length < len) {
+                best = a[i];
+                len = best.length;
+            }
+        }
+        return best;
+    };
+
+    function make_num(num) {
+        var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
+        if (Math.floor(num) === num) {
+            if (num >= 0) {
+                a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
+                       "0" + num.toString(8)); // same.
+            } else {
+                a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
+                       "-0" + (-num).toString(8)); // same.
+            }
+            if ((m = /^(.*?)(0+)$/.exec(num))) {
+                a.push(m[1] + "e" + m[2].length);
+            }
+        } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
+            a.push(m[2] + "e-" + (m[1].length + m[2].length),
+                   str.substr(str.indexOf(".")));
+        }
+        return best_of(a);
+    };
+
+    function make_block(stmt, output) {
+        if (stmt instanceof AST_BlockStatement) {
+            stmt.print(output);
+            return;
+        }
+        output.with_block(function(){
+            output.indent();
+            stmt.print(output);
+            output.newline();
+        });
+    };
+
+    /* -----[ source map generators ]----- */
+
+    function DEFMAP(nodetype, generator) {
+        nodetype.DEFMETHOD("add_source_map", function(stream){
+            generator(this, stream);
+        });
+    };
+
+    // We could easily add info for ALL nodes, but it seems to me that
+    // would be quite wasteful, hence this noop in the base class.
+    DEFMAP(AST_Node, noop);
+
+    function basic_sourcemap_gen(self, output) {
+        output.add_mapping(self.start);
+    };
+
+    // XXX: I'm not exactly sure if we need it for all of these nodes,
+    // or if we should add even more.
+
+    DEFMAP(AST_Directive, basic_sourcemap_gen);
+    DEFMAP(AST_Debugger, basic_sourcemap_gen);
+    DEFMAP(AST_Symbol, basic_sourcemap_gen);
+    DEFMAP(AST_Jump, basic_sourcemap_gen);
+    DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
+    DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
+    DEFMAP(AST_Lambda, basic_sourcemap_gen);
+    DEFMAP(AST_Switch, basic_sourcemap_gen);
+    DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
+    DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
+    DEFMAP(AST_Toplevel, noop);
+    DEFMAP(AST_New, basic_sourcemap_gen);
+    DEFMAP(AST_Try, basic_sourcemap_gen);
+    DEFMAP(AST_Catch, basic_sourcemap_gen);
+    DEFMAP(AST_Finally, basic_sourcemap_gen);
+    DEFMAP(AST_Definitions, basic_sourcemap_gen);
+    DEFMAP(AST_Constant, basic_sourcemap_gen);
+    DEFMAP(AST_ObjectProperty, function(self, output){
+        output.add_mapping(self.start, self.key);
+    });
+
+})();
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/parse.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/parse.js
new file mode 100644
index 0000000..e65c4fa
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/parse.js
@@ -0,0 +1,1486 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+    Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
+var KEYWORDS_ATOM = 'false null true';
+var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'
+    + " " + KEYWORDS_ATOM + " " + KEYWORDS;
+var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
+
+KEYWORDS = makePredicate(KEYWORDS);
+RESERVED_WORDS = makePredicate(RESERVED_WORDS);
+KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
+KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
+
+var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
+
+var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
+var RE_OCT_NUMBER = /^0[0-7]+$/;
+var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
+
+var OPERATORS = makePredicate([
+    "in",
+    "instanceof",
+    "typeof",
+    "new",
+    "void",
+    "delete",
+    "++",
+    "--",
+    "+",
+    "-",
+    "!",
+    "~",
+    "&",
+    "|",
+    "^",
+    "*",
+    "/",
+    "%",
+    ">>",
+    "<<",
+    ">>>",
+    "<",
+    ">",
+    "<=",
+    ">=",
+    "==",
+    "===",
+    "!=",
+    "!==",
+    "?",
+    "=",
+    "+=",
+    "-=",
+    "/=",
+    "*=",
+    "%=",
+    ">>=",
+    "<<=",
+    ">>>=",
+    "|=",
+    "^=",
+    "&=",
+    "&&",
+    "||"
+]);
+
+var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
+
+var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
+
+var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
+
+var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
+
+/* -----[ Tokenizer ]----- */
+
+// regexps adapted from http://xregexp.com/plugins/#unicode
+var UNICODE = {
+    letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
+    digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]"),
+    non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
+    space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),
+    connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
+};
+
+function is_letter(code) {
+    return (code >= 97 && code <= 122)
+        || (code >= 65 && code <= 90)
+        || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
+};
+
+function is_digit(code) {
+    return code >= 48 && code <= 57;
+};
+
+function is_alphanumeric_char(code) {
+    return is_digit(code) || is_letter(code);
+};
+
+function is_unicode_digit(code) {
+    return UNICODE.digit.test(String.fromCharCode(code));
+}
+
+function is_unicode_combining_mark(ch) {
+    return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
+};
+
+function is_unicode_connector_punctuation(ch) {
+    return UNICODE.connector_punctuation.test(ch);
+};
+
+function is_identifier(name) {
+    return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
+};
+
+function is_identifier_start(code) {
+    return code == 36 || code == 95 || is_letter(code);
+};
+
+function is_identifier_char(ch) {
+    var code = ch.charCodeAt(0);
+    return is_identifier_start(code)
+        || is_digit(code)
+        || code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
+        || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
+        || is_unicode_combining_mark(ch)
+        || is_unicode_connector_punctuation(ch)
+        || is_unicode_digit(code)
+    ;
+};
+
+function is_identifier_string(str){
+    return /^[a-z_$][a-z0-9_$]*$/i.test(str);
+};
+
+function parse_js_number(num) {
+    if (RE_HEX_NUMBER.test(num)) {
+        return parseInt(num.substr(2), 16);
+    } else if (RE_OCT_NUMBER.test(num)) {
+        return parseInt(num.substr(1), 8);
+    } else if (RE_DEC_NUMBER.test(num)) {
+        return parseFloat(num);
+    }
+};
+
+function JS_Parse_Error(message, filename, line, col, pos) {
+    this.message = message;
+    this.filename = filename;
+    this.line = line;
+    this.col = col;
+    this.pos = pos;
+    this.stack = new Error().stack;
+};
+
+JS_Parse_Error.prototype.toString = function() {
+    return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
+};
+
+function js_error(message, filename, line, col, pos) {
+    throw new JS_Parse_Error(message, filename, line, col, pos);
+};
+
+function is_token(token, type, val) {
+    return token.type == type && (val == null || token.value == val);
+};
+
+var EX_EOF = {};
+
+function tokenizer($TEXT, filename, html5_comments) {
+
+    var S = {
+        text            : $TEXT,
+        filename        : filename,
+        pos             : 0,
+        tokpos          : 0,
+        line            : 1,
+        tokline         : 0,
+        col             : 0,
+        tokcol          : 0,
+        newline_before  : false,
+        regex_allowed   : false,
+        comments_before : []
+    };
+
+    function peek() { return S.text.charAt(S.pos); };
+
+    function next(signal_eof, in_string) {
+        var ch = S.text.charAt(S.pos++);
+        if (signal_eof && !ch)
+            throw EX_EOF;
+        if ("\r\n\u2028\u2029".indexOf(ch) >= 0) {
+            S.newline_before = S.newline_before || !in_string;
+            ++S.line;
+            S.col = 0;
+            if (!in_string && ch == "\r" && peek() == "\n") {
+                // treat a \r\n sequence as a single \n
+                ++S.pos;
+                ch = "\n";
+            }
+        } else {
+            ++S.col;
+        }
+        return ch;
+    };
+
+    function forward(i) {
+        while (i-- > 0) next();
+    };
+
+    function looking_at(str) {
+        return S.text.substr(S.pos, str.length) == str;
+    };
+
+    function find(what, signal_eof) {
+        var pos = S.text.indexOf(what, S.pos);
+        if (signal_eof && pos == -1) throw EX_EOF;
+        return pos;
+    };
+
+    function start_token() {
+        S.tokline = S.line;
+        S.tokcol = S.col;
+        S.tokpos = S.pos;
+    };
+
+    var prev_was_dot = false;
+    function token(type, value, is_comment) {
+        S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
+                           (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
+                           (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
+        prev_was_dot = (type == "punc" && value == ".");
+        var ret = {
+            type    : type,
+            value   : value,
+            line    : S.tokline,
+            col     : S.tokcol,
+            pos     : S.tokpos,
+            endline : S.line,
+            endcol  : S.col,
+            endpos  : S.pos,
+            nlb     : S.newline_before,
+            file    : filename
+        };
+        if (!is_comment) {
+            ret.comments_before = S.comments_before;
+            S.comments_before = [];
+            // make note of any newlines in the comments that came before
+            for (var i = 0, len = ret.comments_before.length; i < len; i++) {
+                ret.nlb = ret.nlb || ret.comments_before[i].nlb;
+            }
+        }
+        S.newline_before = false;
+        return new AST_Token(ret);
+    };
+
+    function skip_whitespace() {
+        var ch;
+        while (WHITESPACE_CHARS(ch = peek()) || ch == "\u2028" || ch == "\u2029")
+            next();
+    };
+
+    function read_while(pred) {
+        var ret = "", ch, i = 0;
+        while ((ch = peek()) && pred(ch, i++))
+            ret += next();
+        return ret;
+    };
+
+    function parse_error(err) {
+        js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
+    };
+
+    function read_num(prefix) {
+        var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
+        var num = read_while(function(ch, i){
+            var code = ch.charCodeAt(0);
+            switch (code) {
+              case 120: case 88: // xX
+                return has_x ? false : (has_x = true);
+              case 101: case 69: // eE
+                return has_x ? true : has_e ? false : (has_e = after_e = true);
+              case 45: // -
+                return after_e || (i == 0 && !prefix);
+              case 43: // +
+                return after_e;
+              case (after_e = false, 46): // .
+                return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
+            }
+            return is_alphanumeric_char(code);
+        });
+        if (prefix) num = prefix + num;
+        var valid = parse_js_number(num);
+        if (!isNaN(valid)) {
+            return token("num", valid);
+        } else {
+            parse_error("Invalid syntax: " + num);
+        }
+    };
+
+    function read_escaped_char(in_string) {
+        var ch = next(true, in_string);
+        switch (ch.charCodeAt(0)) {
+          case 110 : return "\n";
+          case 114 : return "\r";
+          case 116 : return "\t";
+          case 98  : return "\b";
+          case 118 : return "\u000b"; // \v
+          case 102 : return "\f";
+          case 48  : return "\0";
+          case 120 : return String.fromCharCode(hex_bytes(2)); // \x
+          case 117 : return String.fromCharCode(hex_bytes(4)); // \u
+          case 10  : return ""; // newline
+          case 13  :            // \r
+            if (peek() == "\n") { // DOS newline
+                next(true, in_string);
+                return "";
+            }
+        }
+        return ch;
+    };
+
+    function hex_bytes(n) {
+        var num = 0;
+        for (; n > 0; --n) {
+            var digit = parseInt(next(true), 16);
+            if (isNaN(digit))
+                parse_error("Invalid hex-character pattern in string");
+            num = (num << 4) | digit;
+        }
+        return num;
+    };
+
+    var read_string = with_eof_error("Unterminated string constant", function(quote_char){
+        var quote = next(), ret = "";
+        for (;;) {
+            var ch = next(true, true);
+            if (ch == "\\") {
+                // read OctalEscapeSequence (XXX: deprecated if "strict mode")
+                // https://github.com/mishoo/UglifyJS/issues/178
+                var octal_len = 0, first = null;
+                ch = read_while(function(ch){
+                    if (ch >= "0" && ch <= "7") {
+                        if (!first) {
+                            first = ch;
+                            return ++octal_len;
+                        }
+                        else if (first <= "3" && octal_len <= 2) return ++octal_len;
+                        else if (first >= "4" && octal_len <= 1) return ++octal_len;
+                    }
+                    return false;
+                });
+                if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
+                else ch = read_escaped_char(true);
+            }
+            else if (ch == quote) break;
+            ret += ch;
+        }
+        var tok = token("string", ret);
+        tok.quote = quote_char;
+        return tok;
+    });
+
+    function skip_line_comment(type) {
+        var regex_allowed = S.regex_allowed;
+        var i = find("\n"), ret;
+        if (i == -1) {
+            ret = S.text.substr(S.pos);
+            S.pos = S.text.length;
+        } else {
+            ret = S.text.substring(S.pos, i);
+            S.pos = i;
+        }
+        S.col = S.tokcol + (S.pos - S.tokpos);
+        S.comments_before.push(token(type, ret, true));
+        S.regex_allowed = regex_allowed;
+        return next_token();
+    };
+
+    var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
+        var regex_allowed = S.regex_allowed;
+        var i = find("*/", true);
+        var text = S.text.substring(S.pos, i);
+        var a = text.split("\n"), n = a.length;
+        // update stream position
+        S.pos = i + 2;
+        S.line += n - 1;
+        if (n > 1) S.col = a[n - 1].length;
+        else S.col += a[n - 1].length;
+        S.col += 2;
+        var nlb = S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
+        S.comments_before.push(token("comment2", text, true));
+        S.regex_allowed = regex_allowed;
+        S.newline_before = nlb;
+        return next_token();
+    });
+
+    function read_name() {
+        var backslash = false, name = "", ch, escaped = false, hex;
+        while ((ch = peek()) != null) {
+            if (!backslash) {
+                if (ch == "\\") escaped = backslash = true, next();
+                else if (is_identifier_char(ch)) name += next();
+                else break;
+            }
+            else {
+                if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
+                ch = read_escaped_char();
+                if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
+                name += ch;
+                backslash = false;
+            }
+        }
+        if (KEYWORDS(name) && escaped) {
+            hex = name.charCodeAt(0).toString(16).toUpperCase();
+            name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
+        }
+        return name;
+    };
+
+    var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
+        var prev_backslash = false, ch, in_class = false;
+        while ((ch = next(true))) if (prev_backslash) {
+            regexp += "\\" + ch;
+            prev_backslash = false;
+        } else if (ch == "[") {
+            in_class = true;
+            regexp += ch;
+        } else if (ch == "]" && in_class) {
+            in_class = false;
+            regexp += ch;
+        } else if (ch == "/" && !in_class) {
+            break;
+        } else if (ch == "\\") {
+            prev_backslash = true;
+        } else {
+            regexp += ch;
+        }
+        var mods = read_name();
+        return token("regexp", new RegExp(regexp, mods));
+    });
+
+    function read_operator(prefix) {
+        function grow(op) {
+            if (!peek()) return op;
+            var bigger = op + peek();
+            if (OPERATORS(bigger)) {
+                next();
+                return grow(bigger);
+            } else {
+                return op;
+            }
+        };
+        return token("operator", grow(prefix || next()));
+    };
+
+    function handle_slash() {
+        next();
+        switch (peek()) {
+          case "/":
+            next();
+            return skip_line_comment("comment1");
+          case "*":
+            next();
+            return skip_multiline_comment();
+        }
+        return S.regex_allowed ? read_regexp("") : read_operator("/");
+    };
+
+    function handle_dot() {
+        next();
+        return is_digit(peek().charCodeAt(0))
+            ? read_num(".")
+            : token("punc", ".");
+    };
+
+    function read_word() {
+        var word = read_name();
+        if (prev_was_dot) return token("name", word);
+        return KEYWORDS_ATOM(word) ? token("atom", word)
+            : !KEYWORDS(word) ? token("name", word)
+            : OPERATORS(word) ? token("operator", word)
+            : token("keyword", word);
+    };
+
+    function with_eof_error(eof_error, cont) {
+        return function(x) {
+            try {
+                return cont(x);
+            } catch(ex) {
+                if (ex === EX_EOF) parse_error(eof_error);
+                else throw ex;
+            }
+        };
+    };
+
+    function next_token(force_regexp) {
+        if (force_regexp != null)
+            return read_regexp(force_regexp);
+        skip_whitespace();
+        start_token();
+        if (html5_comments) {
+            if (looking_at("<!--")) {
+                forward(4);
+                return skip_line_comment("comment3");
+            }
+            if (looking_at("-->") && S.newline_before) {
+                forward(3);
+                return skip_line_comment("comment4");
+            }
+        }
+        var ch = peek();
+        if (!ch) return token("eof");
+        var code = ch.charCodeAt(0);
+        switch (code) {
+          case 34: case 39: return read_string(ch);
+          case 46: return handle_dot();
+          case 47: return handle_slash();
+        }
+        if (is_digit(code)) return read_num();
+        if (PUNC_CHARS(ch)) return token("punc", next());
+        if (OPERATOR_CHARS(ch)) return read_operator();
+        if (code == 92 || is_identifier_start(code)) return read_word();
+        parse_error("Unexpected character '" + ch + "'");
+    };
+
+    next_token.context = function(nc) {
+        if (nc) S = nc;
+        return S;
+    };
+
+    return next_token;
+
+};
+
+/* -----[ Parser (constants) ]----- */
+
+var UNARY_PREFIX = makePredicate([
+    "typeof",
+    "void",
+    "delete",
+    "--",
+    "++",
+    "!",
+    "~",
+    "-",
+    "+"
+]);
+
+var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
+
+var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
+
+var PRECEDENCE = (function(a, ret){
+    for (var i = 0; i < a.length; ++i) {
+        var b = a[i];
+        for (var j = 0; j < b.length; ++j) {
+            ret[b[j]] = i + 1;
+        }
+    }
+    return ret;
+})(
+    [
+        ["||"],
+        ["&&"],
+        ["|"],
+        ["^"],
+        ["&"],
+        ["==", "===", "!=", "!=="],
+        ["<", ">", "<=", ">=", "in", "instanceof"],
+        [">>", "<<", ">>>"],
+        ["+", "-"],
+        ["*", "/", "%"]
+    ],
+    {}
+);
+
+var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
+
+var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
+
+/* -----[ Parser ]----- */
+
+function parse($TEXT, options) {
+
+    options = defaults(options, {
+        strict         : false,
+        filename       : null,
+        toplevel       : null,
+        expression     : false,
+        html5_comments : true,
+        bare_returns   : false,
+    });
+
+    var S = {
+        input         : (typeof $TEXT == "string"
+                         ? tokenizer($TEXT, options.filename,
+                                     options.html5_comments)
+                         : $TEXT),
+        token         : null,
+        prev          : null,
+        peeked        : null,
+        in_function   : 0,
+        in_directives : true,
+        in_loop       : 0,
+        labels        : []
+    };
+
+    S.token = next();
+
+    function is(type, value) {
+        return is_token(S.token, type, value);
+    };
+
+    function peek() { return S.peeked || (S.peeked = S.input()); };
+
+    function next() {
+        S.prev = S.token;
+        if (S.peeked) {
+            S.token = S.peeked;
+            S.peeked = null;
+        } else {
+            S.token = S.input();
+        }
+        S.in_directives = S.in_directives && (
+            S.token.type == "string" || is("punc", ";")
+        );
+        return S.token;
+    };
+
+    function prev() {
+        return S.prev;
+    };
+
+    function croak(msg, line, col, pos) {
+        var ctx = S.input.context();
+        js_error(msg,
+                 ctx.filename,
+                 line != null ? line : ctx.tokline,
+                 col != null ? col : ctx.tokcol,
+                 pos != null ? pos : ctx.tokpos);
+    };
+
+    function token_error(token, msg) {
+        croak(msg, token.line, token.col);
+    };
+
+    function unexpected(token) {
+        if (token == null)
+            token = S.token;
+        token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
+    };
+
+    function expect_token(type, val) {
+        if (is(type, val)) {
+            return next();
+        }
+        token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
+    };
+
+    function expect(punc) { return expect_token("punc", punc); };
+
+    function can_insert_semicolon() {
+        return !options.strict && (
+            S.token.nlb || is("eof") || is("punc", "}")
+        );
+    };
+
+    function semicolon() {
+        if (is("punc", ";")) next();
+        else if (!can_insert_semicolon()) unexpected();
+    };
+
+    function parenthesised() {
+        expect("(");
+        var exp = expression(true);
+        expect(")");
+        return exp;
+    };
+
+    function embed_tokens(parser) {
+        return function() {
+            var start = S.token;
+            var expr = parser();
+            var end = prev();
+            expr.start = start;
+            expr.end = end;
+            return expr;
+        };
+    };
+
+    function handle_regexp() {
+        if (is("operator", "/") || is("operator", "/=")) {
+            S.peeked = null;
+            S.token = S.input(S.token.value.substr(1)); // force regexp
+        }
+    };
+
+    var statement = embed_tokens(function() {
+        var tmp;
+        handle_regexp();
+        switch (S.token.type) {
+          case "string":
+            var dir = S.in_directives, stat = simple_statement();
+            // XXXv2: decide how to fix directives
+            if (dir && stat.body instanceof AST_String && !is("punc", ",")) {
+                return new AST_Directive({
+                    start : stat.body.start,
+                    end   : stat.body.end,
+                    quote : stat.body.quote,
+                    value : stat.body.value,
+                });
+            }
+            return stat;
+          case "num":
+          case "regexp":
+          case "operator":
+          case "atom":
+            return simple_statement();
+
+          case "name":
+            return is_token(peek(), "punc", ":")
+                ? labeled_statement()
+                : simple_statement();
+
+          case "punc":
+            switch (S.token.value) {
+              case "{":
+                return new AST_BlockStatement({
+                    start : S.token,
+                    body  : block_(),
+                    end   : prev()
+                });
+              case "[":
+              case "(":
+                return simple_statement();
+              case ";":
+                next();
+                return new AST_EmptyStatement();
+              default:
+                unexpected();
+            }
+
+          case "keyword":
+            switch (tmp = S.token.value, next(), tmp) {
+              case "break":
+                return break_cont(AST_Break);
+
+              case "continue":
+                return break_cont(AST_Continue);
+
+              case "debugger":
+                semicolon();
+                return new AST_Debugger();
+
+              case "do":
+                return new AST_Do({
+                    body      : in_loop(statement),
+                    condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
+                });
+
+              case "while":
+                return new AST_While({
+                    condition : parenthesised(),
+                    body      : in_loop(statement)
+                });
+
+              case "for":
+                return for_();
+
+              case "function":
+                return function_(AST_Defun);
+
+              case "if":
+                return if_();
+
+              case "return":
+                if (S.in_function == 0 && !options.bare_returns)
+                    croak("'return' outside of function");
+                return new AST_Return({
+                    value: ( is("punc", ";")
+                             ? (next(), null)
+                             : can_insert_semicolon()
+                             ? null
+                             : (tmp = expression(true), semicolon(), tmp) )
+                });
+
+              case "switch":
+                return new AST_Switch({
+                    expression : parenthesised(),
+                    body       : in_loop(switch_body_)
+                });
+
+              case "throw":
+                if (S.token.nlb)
+                    croak("Illegal newline after 'throw'");
+                return new AST_Throw({
+                    value: (tmp = expression(true), semicolon(), tmp)
+                });
+
+              case "try":
+                return try_();
+
+              case "var":
+                return tmp = var_(), semicolon(), tmp;
+
+              case "const":
+                return tmp = const_(), semicolon(), tmp;
+
+              case "with":
+                return new AST_With({
+                    expression : parenthesised(),
+                    body       : statement()
+                });
+
+              default:
+                unexpected();
+            }
+        }
+    });
+
+    function labeled_statement() {
+        var label = as_symbol(AST_Label);
+        if (find_if(function(l){ return l.name == label.name }, S.labels)) {
+            // ECMA-262, 12.12: An ECMAScript program is considered
+            // syntactically incorrect if it contains a
+            // LabelledStatement that is enclosed by a
+            // LabelledStatement with the same Identifier as label.
+            croak("Label " + label.name + " defined twice");
+        }
+        expect(":");
+        S.labels.push(label);
+        var stat = statement();
+        S.labels.pop();
+        if (!(stat instanceof AST_IterationStatement)) {
+            // check for `continue` that refers to this label.
+            // those should be reported as syntax errors.
+            // https://github.com/mishoo/UglifyJS2/issues/287
+            label.references.forEach(function(ref){
+                if (ref instanceof AST_Continue) {
+                    ref = ref.label.start;
+                    croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
+                          ref.line, ref.col, ref.pos);
+                }
+            });
+        }
+        return new AST_LabeledStatement({ body: stat, label: label });
+    };
+
+    function simple_statement(tmp) {
+        return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
+    };
+
+    function break_cont(type) {
+        var label = null, ldef;
+        if (!can_insert_semicolon()) {
+            label = as_symbol(AST_LabelRef, true);
+        }
+        if (label != null) {
+            ldef = find_if(function(l){ return l.name == label.name }, S.labels);
+            if (!ldef)
+                croak("Undefined label " + label.name);
+            label.thedef = ldef;
+        }
+        else if (S.in_loop == 0)
+            croak(type.TYPE + " not inside a loop or switch");
+        semicolon();
+        var stat = new type({ label: label });
+        if (ldef) ldef.references.push(stat);
+        return stat;
+    };
+
+    function for_() {
+        expect("(");
+        var init = null;
+        if (!is("punc", ";")) {
+            init = is("keyword", "var")
+                ? (next(), var_(true))
+                : expression(true, true);
+            if (is("operator", "in")) {
+                if (init instanceof AST_Var && init.definitions.length > 1)
+                    croak("Only one variable declaration allowed in for..in loop");
+                next();
+                return for_in(init);
+            }
+        }
+        return regular_for(init);
+    };
+
+    function regular_for(init) {
+        expect(";");
+        var test = is("punc", ";") ? null : expression(true);
+        expect(";");
+        var step = is("punc", ")") ? null : expression(true);
+        expect(")");
+        return new AST_For({
+            init      : init,
+            condition : test,
+            step      : step,
+            body      : in_loop(statement)
+        });
+    };
+
+    function for_in(init) {
+        var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
+        var obj = expression(true);
+        expect(")");
+        return new AST_ForIn({
+            init   : init,
+            name   : lhs,
+            object : obj,
+            body   : in_loop(statement)
+        });
+    };
+
+    var function_ = function(ctor) {
+        var in_statement = ctor === AST_Defun;
+        var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
+        if (in_statement && !name)
+            unexpected();
+        expect("(");
+        return new ctor({
+            name: name,
+            argnames: (function(first, a){
+                while (!is("punc", ")")) {
+                    if (first) first = false; else expect(",");
+                    a.push(as_symbol(AST_SymbolFunarg));
+                }
+                next();
+                return a;
+            })(true, []),
+            body: (function(loop, labels){
+                ++S.in_function;
+                S.in_directives = true;
+                S.in_loop = 0;
+                S.labels = [];
+                var a = block_();
+                --S.in_function;
+                S.in_loop = loop;
+                S.labels = labels;
+                return a;
+            })(S.in_loop, S.labels)
+        });
+    };
+
+    function if_() {
+        var cond = parenthesised(), body = statement(), belse = null;
+        if (is("keyword", "else")) {
+            next();
+            belse = statement();
+        }
+        return new AST_If({
+            condition   : cond,
+            body        : body,
+            alternative : belse
+        });
+    };
+
+    function block_() {
+        expect("{");
+        var a = [];
+        while (!is("punc", "}")) {
+            if (is("eof")) unexpected();
+            a.push(statement());
+        }
+        next();
+        return a;
+    };
+
+    function switch_body_() {
+        expect("{");
+        var a = [], cur = null, branch = null, tmp;
+        while (!is("punc", "}")) {
+            if (is("eof")) unexpected();
+            if (is("keyword", "case")) {
+                if (branch) branch.end = prev();
+                cur = [];
+                branch = new AST_Case({
+                    start      : (tmp = S.token, next(), tmp),
+                    expression : expression(true),
+                    body       : cur
+                });
+                a.push(branch);
+                expect(":");
+            }
+            else if (is("keyword", "default")) {
+                if (branch) branch.end = prev();
+                cur = [];
+                branch = new AST_Default({
+                    start : (tmp = S.token, next(), expect(":"), tmp),
+                    body  : cur
+                });
+                a.push(branch);
+            }
+            else {
+                if (!cur) unexpected();
+                cur.push(statement());
+            }
+        }
+        if (branch) branch.end = prev();
+        next();
+        return a;
+    };
+
+    function try_() {
+        var body = block_(), bcatch = null, bfinally = null;
+        if (is("keyword", "catch")) {
+            var start = S.token;
+            next();
+            expect("(");
+            var name = as_symbol(AST_SymbolCatch);
+            expect(")");
+            bcatch = new AST_Catch({
+                start   : start,
+                argname : name,
+                body    : block_(),
+                end     : prev()
+            });
+        }
+        if (is("keyword", "finally")) {
+            var start = S.token;
+            next();
+            bfinally = new AST_Finally({
+                start : start,
+                body  : block_(),
+                end   : prev()
+            });
+        }
+        if (!bcatch && !bfinally)
+            croak("Missing catch/finally blocks");
+        return new AST_Try({
+            body     : body,
+            bcatch   : bcatch,
+            bfinally : bfinally
+        });
+    };
+
+    function vardefs(no_in, in_const) {
+        var a = [];
+        for (;;) {
+            a.push(new AST_VarDef({
+                start : S.token,
+                name  : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
+                value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
+                end   : prev()
+            }));
+            if (!is("punc", ","))
+                break;
+            next();
+        }
+        return a;
+    };
+
+    var var_ = function(no_in) {
+        return new AST_Var({
+            start       : prev(),
+            definitions : vardefs(no_in, false),
+            end         : prev()
+        });
+    };
+
+    var const_ = function() {
+        return new AST_Const({
+            start       : prev(),
+            definitions : vardefs(false, true),
+            end         : prev()
+        });
+    };
+
+    var new_ = function() {
+        var start = S.token;
+        expect_token("operator", "new");
+        var newexp = expr_atom(false), args;
+        if (is("punc", "(")) {
+            next();
+            args = expr_list(")");
+        } else {
+            args = [];
+        }
+        return subscripts(new AST_New({
+            start      : start,
+            expression : newexp,
+            args       : args,
+            end        : prev()
+        }), true);
+    };
+
+    function as_atom_node() {
+        var tok = S.token, ret;
+        switch (tok.type) {
+          case "name":
+          case "keyword":
+            ret = _make_symbol(AST_SymbolRef);
+            break;
+          case "num":
+            ret = new AST_Number({ start: tok, end: tok, value: tok.value });
+            break;
+          case "string":
+            ret = new AST_String({
+                start : tok,
+                end   : tok,
+                value : tok.value,
+                quote : tok.quote
+            });
+            break;
+          case "regexp":
+            ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
+            break;
+          case "atom":
+            switch (tok.value) {
+              case "false":
+                ret = new AST_False({ start: tok, end: tok });
+                break;
+              case "true":
+                ret = new AST_True({ start: tok, end: tok });
+                break;
+              case "null":
+                ret = new AST_Null({ start: tok, end: tok });
+                break;
+            }
+            break;
+        }
+        next();
+        return ret;
+    };
+
+    var expr_atom = function(allow_calls) {
+        if (is("operator", "new")) {
+            return new_();
+        }
+        var start = S.token;
+        if (is("punc")) {
+            switch (start.value) {
+              case "(":
+                next();
+                var ex = expression(true);
+                ex.start = start;
+                ex.end = S.token;
+                expect(")");
+                return subscripts(ex, allow_calls);
+              case "[":
+                return subscripts(array_(), allow_calls);
+              case "{":
+                return subscripts(object_(), allow_calls);
+            }
+            unexpected();
+        }
+        if (is("keyword", "function")) {
+            next();
+            var func = function_(AST_Function);
+            func.start = start;
+            func.end = prev();
+            return subscripts(func, allow_calls);
+        }
+        if (ATOMIC_START_TOKEN[S.token.type]) {
+            return subscripts(as_atom_node(), allow_calls);
+        }
+        unexpected();
+    };
+
+    function expr_list(closing, allow_trailing_comma, allow_empty) {
+        var first = true, a = [];
+        while (!is("punc", closing)) {
+            if (first) first = false; else expect(",");
+            if (allow_trailing_comma && is("punc", closing)) break;
+            if (is("punc", ",") && allow_empty) {
+                a.push(new AST_Hole({ start: S.token, end: S.token }));
+            } else {
+                a.push(expression(false));
+            }
+        }
+        next();
+        return a;
+    };
+
+    var array_ = embed_tokens(function() {
+        expect("[");
+        return new AST_Array({
+            elements: expr_list("]", !options.strict, true)
+        });
+    });
+
+    var object_ = embed_tokens(function() {
+        expect("{");
+        var first = true, a = [];
+        while (!is("punc", "}")) {
+            if (first) first = false; else expect(",");
+            if (!options.strict && is("punc", "}"))
+                // allow trailing comma
+                break;
+            var start = S.token;
+            var type = start.type;
+            var name = as_property_name();
+            if (type == "name" && !is("punc", ":")) {
+                if (name == "get") {
+                    a.push(new AST_ObjectGetter({
+                        start : start,
+                        key   : as_atom_node(),
+                        value : function_(AST_Accessor),
+                        end   : prev()
+                    }));
+                    continue;
+                }
+                if (name == "set") {
+                    a.push(new AST_ObjectSetter({
+                        start : start,
+                        key   : as_atom_node(),
+                        value : function_(AST_Accessor),
+                        end   : prev()
+                    }));
+                    continue;
+                }
+            }
+            expect(":");
+            a.push(new AST_ObjectKeyVal({
+                start : start,
+                quote : start.quote,
+                key   : name,
+                value : expression(false),
+                end   : prev()
+            }));
+        }
+        next();
+        return new AST_Object({ properties: a });
+    });
+
+    function as_property_name() {
+        var tmp = S.token;
+        next();
+        switch (tmp.type) {
+          case "num":
+          case "string":
+          case "name":
+          case "operator":
+          case "keyword":
+          case "atom":
+            return tmp.value;
+          default:
+            unexpected();
+        }
+    };
+
+    function as_name() {
+        var tmp = S.token;
+        next();
+        switch (tmp.type) {
+          case "name":
+          case "operator":
+          case "keyword":
+          case "atom":
+            return tmp.value;
+          default:
+            unexpected();
+        }
+    };
+
+    function _make_symbol(type) {
+        var name = S.token.value;
+        return new (name == "this" ? AST_This : type)({
+            name  : String(name),
+            start : S.token,
+            end   : S.token
+        });
+    };
+
+    function as_symbol(type, noerror) {
+        if (!is("name")) {
+            if (!noerror) croak("Name expected");
+            return null;
+        }
+        var sym = _make_symbol(type);
+        next();
+        return sym;
+    };
+
+    var subscripts = function(expr, allow_calls) {
+        var start = expr.start;
+        if (is("punc", ".")) {
+            next();
+            return subscripts(new AST_Dot({
+                start      : start,
+                expression : expr,
+                property   : as_name(),
+                end        : prev()
+            }), allow_calls);
+        }
+        if (is("punc", "[")) {
+            next();
+            var prop = expression(true);
+            expect("]");
+            return subscripts(new AST_Sub({
+                start      : start,
+                expression : expr,
+                property   : prop,
+                end        : prev()
+            }), allow_calls);
+        }
+        if (allow_calls && is("punc", "(")) {
+            next();
+            return subscripts(new AST_Call({
+                start      : start,
+                expression : expr,
+                args       : expr_list(")"),
+                end        : prev()
+            }), true);
+        }
+        return expr;
+    };
+
+    var maybe_unary = function(allow_calls) {
+        var start = S.token;
+        if (is("operator") && UNARY_PREFIX(start.value)) {
+            next();
+            handle_regexp();
+            var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
+            ex.start = start;
+            ex.end = prev();
+            return ex;
+        }
+        var val = expr_atom(allow_calls);
+        while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
+            val = make_unary(AST_UnaryPostfix, S.token.value, val);
+            val.start = start;
+            val.end = S.token;
+            next();
+        }
+        return val;
+    };
+
+    function make_unary(ctor, op, expr) {
+        if ((op == "++" || op == "--") && !is_assignable(expr))
+            croak("Invalid use of " + op + " operator");
+        return new ctor({ operator: op, expression: expr });
+    };
+
+    var expr_op = function(left, min_prec, no_in) {
+        var op = is("operator") ? S.token.value : null;
+        if (op == "in" && no_in) op = null;
+        var prec = op != null ? PRECEDENCE[op] : null;
+        if (prec != null && prec > min_prec) {
+            next();
+            var right = expr_op(maybe_unary(true), prec, no_in);
+            return expr_op(new AST_Binary({
+                start    : left.start,
+                left     : left,
+                operator : op,
+                right    : right,
+                end      : right.end
+            }), min_prec, no_in);
+        }
+        return left;
+    };
+
+    function expr_ops(no_in) {
+        return expr_op(maybe_unary(true), 0, no_in);
+    };
+
+    var maybe_conditional = function(no_in) {
+        var start = S.token;
+        var expr = expr_ops(no_in);
+        if (is("operator", "?")) {
+            next();
+            var yes = expression(false);
+            expect(":");
+            return new AST_Conditional({
+                start       : start,
+                condition   : expr,
+                consequent  : yes,
+                alternative : expression(false, no_in),
+                end         : prev()
+            });
+        }
+        return expr;
+    };
+
+    function is_assignable(expr) {
+        if (!options.strict) return true;
+        if (expr instanceof AST_This) return false;
+        return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
+    };
+
+    var maybe_assign = function(no_in) {
+        var start = S.token;
+        var left = maybe_conditional(no_in), val = S.token.value;
+        if (is("operator") && ASSIGNMENT(val)) {
+            if (is_assignable(left)) {
+                next();
+                return new AST_Assign({
+                    start    : start,
+                    left     : left,
+                    operator : val,
+                    right    : maybe_assign(no_in),
+                    end      : prev()
+                });
+            }
+            croak("Invalid assignment");
+        }
+        return left;
+    };
+
+    var expression = function(commas, no_in) {
+        var start = S.token;
+        var expr = maybe_assign(no_in);
+        if (commas && is("punc", ",")) {
+            next();
+            return new AST_Seq({
+                start  : start,
+                car    : expr,
+                cdr    : expression(true, no_in),
+                end    : peek()
+            });
+        }
+        return expr;
+    };
+
+    function in_loop(cont) {
+        ++S.in_loop;
+        var ret = cont();
+        --S.in_loop;
+        return ret;
+    };
+
+    if (options.expression) {
+        return expression(true);
+    }
+
+    return (function(){
+        var start = S.token;
+        var body = [];
+        while (!is("eof"))
+            body.push(statement());
+        var end = prev();
+        var toplevel = options.toplevel;
+        if (toplevel) {
+            toplevel.body = toplevel.body.concat(body);
+            toplevel.end = end;
+        } else {
+            toplevel = new AST_Toplevel({ start: start, body: body, end: end });
+        }
+        return toplevel;
+    })();
+
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/propmangle.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/propmangle.js
new file mode 100644
index 0000000..086709e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/propmangle.js
@@ -0,0 +1,221 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+function find_builtins() {
+    var a = [];
+    [ Object, Array, Function, Number,
+      String, Boolean, Error, Math,
+      Date, RegExp
+    ].forEach(function(ctor){
+        Object.getOwnPropertyNames(ctor).map(add);
+        if (ctor.prototype) {
+            Object.getOwnPropertyNames(ctor.prototype).map(add);
+        }
+    });
+    function add(name) {
+        push_uniq(a, name);
+    }
+    return a;
+}
+
+function mangle_properties(ast, options) {
+    options = defaults(options, {
+        reserved : null,
+        cache : null,
+        only_cache : false,
+        regex : null
+    });
+
+    var reserved = options.reserved;
+    if (reserved == null)
+        reserved = find_builtins();
+
+    var cache = options.cache;
+    if (cache == null) {
+        cache = {
+            cname: -1,
+            props: new Dictionary()
+        };
+    }
+
+    var regex = options.regex;
+
+    var names_to_mangle = [];
+
+    // step 1: find candidates to mangle
+    ast.walk(new TreeWalker(function(node){
+        if (node instanceof AST_ObjectKeyVal) {
+            add(node.key);
+        }
+        else if (node instanceof AST_ObjectProperty) {
+            // setter or getter, since KeyVal is handled above
+            add(node.key.name);
+        }
+        else if (node instanceof AST_Dot) {
+            if (this.parent() instanceof AST_Assign) {
+                add(node.property);
+            }
+        }
+        else if (node instanceof AST_Sub) {
+            if (this.parent() instanceof AST_Assign) {
+                addStrings(node.property);
+            }
+        }
+    }));
+
+    // step 2: transform the tree, renaming properties
+    return ast.transform(new TreeTransformer(function(node){
+        if (node instanceof AST_ObjectKeyVal) {
+            if (should_mangle(node.key)) {
+                node.key = mangle(node.key);
+            }
+        }
+        else if (node instanceof AST_ObjectProperty) {
+            // setter or getter
+            if (should_mangle(node.key.name)) {
+                node.key.name = mangle(node.key.name);
+            }
+        }
+        else if (node instanceof AST_Dot) {
+            if (should_mangle(node.property)) {
+                node.property = mangle(node.property);
+            }
+        }
+        else if (node instanceof AST_Sub) {
+            node.property = mangleStrings(node.property);
+        }
+        // else if (node instanceof AST_String) {
+        //     if (should_mangle(node.value)) {
+        //         AST_Node.warn(
+        //             "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
+        //                 file : node.start.file,
+        //                 line : node.start.line,
+        //                 col  : node.start.col,
+        //                 prop : node.value
+        //             }
+        //         );
+        //     }
+        // }
+    }));
+
+    // only function declarations after this line
+
+    function can_mangle(name) {
+        if (reserved.indexOf(name) >= 0) return false;
+        if (options.only_cache) {
+            return cache.props.has(name);
+        }
+        if (/^[0-9.]+$/.test(name)) return false;
+        return true;
+    }
+
+    function should_mangle(name) {
+        if (regex && !regex.test(name)) return false;
+        if (reserved.indexOf(name) >= 0) return false;
+        return cache.props.has(name)
+            || names_to_mangle.indexOf(name) >= 0;
+    }
+
+    function add(name) {
+        if (can_mangle(name))
+            push_uniq(names_to_mangle, name);
+    }
+
+    function mangle(name) {
+        var mangled = cache.props.get(name);
+        if (!mangled) {
+            do {
+                mangled = base54(++cache.cname);
+            } while (!can_mangle(mangled));
+            cache.props.set(name, mangled);
+        }
+        return mangled;
+    }
+
+    function addStrings(node) {
+        var out = {};
+        try {
+            (function walk(node){
+                node.walk(new TreeWalker(function(node){
+                    if (node instanceof AST_Seq) {
+                        walk(node.cdr);
+                        return true;
+                    }
+                    if (node instanceof AST_String) {
+                        add(node.value);
+                        return true;
+                    }
+                    if (node instanceof AST_Conditional) {
+                        walk(node.consequent);
+                        walk(node.alternative);
+                        return true;
+                    }
+                    throw out;
+                }));
+            })(node);
+        } catch(ex) {
+            if (ex !== out) throw ex;
+        }
+    }
+
+    function mangleStrings(node) {
+        return node.transform(new TreeTransformer(function(node){
+            if (node instanceof AST_Seq) {
+                node.cdr = mangleStrings(node.cdr);
+            }
+            else if (node instanceof AST_String) {
+                if (should_mangle(node.value)) {
+                    node.value = mangle(node.value);
+                }
+            }
+            else if (node instanceof AST_Conditional) {
+                node.consequent = mangleStrings(node.consequent);
+                node.alternative = mangleStrings(node.alternative);
+            }
+            return node;
+        }));
+    }
+
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/scope.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/scope.js
new file mode 100644
index 0000000..6c19c19
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/scope.js
@@ -0,0 +1,601 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+function SymbolDef(scope, index, orig) {
+    this.name = orig.name;
+    this.orig = [ orig ];
+    this.scope = scope;
+    this.references = [];
+    this.global = false;
+    this.mangled_name = null;
+    this.undeclared = false;
+    this.constant = false;
+    this.index = index;
+};
+
+SymbolDef.prototype = {
+    unmangleable: function(options) {
+        if (!options) options = {};
+
+        return (this.global && !options.toplevel)
+            || this.undeclared
+            || (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
+            || (options.keep_fnames
+                && (this.orig[0] instanceof AST_SymbolLambda
+                    || this.orig[0] instanceof AST_SymbolDefun));
+    },
+    mangle: function(options) {
+        var cache = options.cache && options.cache.props;
+        if (this.global && cache && cache.has(this.name)) {
+            this.mangled_name = cache.get(this.name);
+        }
+        else if (!this.mangled_name && !this.unmangleable(options)) {
+            var s = this.scope;
+            if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda)
+                s = s.parent_scope;
+            this.mangled_name = s.next_mangled(options, this);
+            if (this.global && cache) {
+                cache.set(this.name, this.mangled_name);
+            }
+        }
+    }
+};
+
+AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
+    options = defaults(options, {
+        screw_ie8: false,
+        cache: null
+    });
+
+    // pass 1: setup scope chaining and handle definitions
+    var self = this;
+    var scope = self.parent_scope = null;
+    var defun = null;
+    var nesting = 0;
+    var tw = new TreeWalker(function(node, descend){
+        if (options.screw_ie8 && node instanceof AST_Catch) {
+            var save_scope = scope;
+            scope = new AST_Scope(node);
+            scope.init_scope_vars(nesting);
+            scope.parent_scope = save_scope;
+            descend();
+            scope = save_scope;
+            return true;
+        }
+        if (node instanceof AST_Scope) {
+            node.init_scope_vars(nesting);
+            var save_scope = node.parent_scope = scope;
+            var save_defun = defun;
+            defun = scope = node;
+            ++nesting; descend(); --nesting;
+            scope = save_scope;
+            defun = save_defun;
+            return true;        // don't descend again in TreeWalker
+        }
+        if (node instanceof AST_Directive) {
+            node.scope = scope;
+            push_uniq(scope.directives, node.value);
+            return true;
+        }
+        if (node instanceof AST_With) {
+            for (var s = scope; s; s = s.parent_scope)
+                s.uses_with = true;
+            return;
+        }
+        if (node instanceof AST_Symbol) {
+            node.scope = scope;
+        }
+        if (node instanceof AST_SymbolLambda) {
+            defun.def_function(node);
+        }
+        else if (node instanceof AST_SymbolDefun) {
+            // Careful here, the scope where this should be defined is
+            // the parent scope.  The reason is that we enter a new
+            // scope when we encounter the AST_Defun node (which is
+            // instanceof AST_Scope) but we get to the symbol a bit
+            // later.
+            (node.scope = defun.parent_scope).def_function(node);
+        }
+        else if (node instanceof AST_SymbolVar
+                 || node instanceof AST_SymbolConst) {
+            var def = defun.def_variable(node);
+            def.constant = node instanceof AST_SymbolConst;
+            def.init = tw.parent().value;
+        }
+        else if (node instanceof AST_SymbolCatch) {
+            (options.screw_ie8 ? scope : defun)
+                .def_variable(node);
+        }
+    });
+    self.walk(tw);
+
+    // pass 2: find back references and eval
+    var func = null;
+    var globals = self.globals = new Dictionary();
+    var tw = new TreeWalker(function(node, descend){
+        if (node instanceof AST_Lambda) {
+            var prev_func = func;
+            func = node;
+            descend();
+            func = prev_func;
+            return true;
+        }
+        if (node instanceof AST_SymbolRef) {
+            var name = node.name;
+            var sym = node.scope.find_variable(name);
+            if (!sym) {
+                var g;
+                if (globals.has(name)) {
+                    g = globals.get(name);
+                } else {
+                    g = new SymbolDef(self, globals.size(), node);
+                    g.undeclared = true;
+                    g.global = true;
+                    globals.set(name, g);
+                }
+                node.thedef = g;
+                if (name == "eval" && tw.parent() instanceof AST_Call) {
+                    for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope)
+                        s.uses_eval = true;
+                }
+                if (func && name == "arguments") {
+                    func.uses_arguments = true;
+                }
+            } else {
+                node.thedef = sym;
+            }
+            node.reference();
+            return true;
+        }
+    });
+    self.walk(tw);
+
+    if (options.cache) {
+        this.cname = options.cache.cname;
+    }
+});
+
+AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
+    this.directives = [];     // contains the directives defined in this scope, i.e. "use strict"
+    this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
+    this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
+    this.uses_with = false;   // will be set to true if this or some nested scope uses the `with` statement
+    this.uses_eval = false;   // will be set to true if this or nested scope uses the global `eval`
+    this.parent_scope = null; // the parent scope
+    this.enclosed = [];       // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
+    this.cname = -1;          // the current index for mangling functions/variables
+    this.nesting = nesting;   // the nesting level of this scope (0 means toplevel)
+});
+
+AST_Scope.DEFMETHOD("strict", function(){
+    return this.has_directive("use strict");
+});
+
+AST_Lambda.DEFMETHOD("init_scope_vars", function(){
+    AST_Scope.prototype.init_scope_vars.apply(this, arguments);
+    this.uses_arguments = false;
+});
+
+AST_SymbolRef.DEFMETHOD("reference", function() {
+    var def = this.definition();
+    def.references.push(this);
+    var s = this.scope;
+    while (s) {
+        push_uniq(s.enclosed, def);
+        if (s === def.scope) break;
+        s = s.parent_scope;
+    }
+    this.frame = this.scope.nesting - def.scope.nesting;
+});
+
+AST_Scope.DEFMETHOD("find_variable", function(name){
+    if (name instanceof AST_Symbol) name = name.name;
+    return this.variables.get(name)
+        || (this.parent_scope && this.parent_scope.find_variable(name));
+});
+
+AST_Scope.DEFMETHOD("has_directive", function(value){
+    return this.parent_scope && this.parent_scope.has_directive(value)
+        || (this.directives.indexOf(value) >= 0 ? this : null);
+});
+
+AST_Scope.DEFMETHOD("def_function", function(symbol){
+    this.functions.set(symbol.name, this.def_variable(symbol));
+});
+
+AST_Scope.DEFMETHOD("def_variable", function(symbol){
+    var def;
+    if (!this.variables.has(symbol.name)) {
+        def = new SymbolDef(this, this.variables.size(), symbol);
+        this.variables.set(symbol.name, def);
+        def.global = !this.parent_scope;
+    } else {
+        def = this.variables.get(symbol.name);
+        def.orig.push(symbol);
+    }
+    return symbol.thedef = def;
+});
+
+AST_Scope.DEFMETHOD("next_mangled", function(options){
+    var ext = this.enclosed;
+    out: while (true) {
+        var m = base54(++this.cname);
+        if (!is_identifier(m)) continue; // skip over "do"
+
+        // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
+        // shadow a name excepted from mangling.
+        if (options.except.indexOf(m) >= 0) continue;
+
+        // we must ensure that the mangled name does not shadow a name
+        // from some parent scope that is referenced in this or in
+        // inner scopes.
+        for (var i = ext.length; --i >= 0;) {
+            var sym = ext[i];
+            var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
+            if (m == name) continue out;
+        }
+        return m;
+    }
+});
+
+AST_Function.DEFMETHOD("next_mangled", function(options, def){
+    // #179, #326
+    // in Safari strict mode, something like (function x(x){...}) is a syntax error;
+    // a function expression's argument cannot shadow the function expression's name
+
+    var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
+    while (true) {
+        var name = AST_Lambda.prototype.next_mangled.call(this, options, def);
+        if (!(tricky_def && tricky_def.mangled_name == name))
+            return name;
+    }
+});
+
+AST_Scope.DEFMETHOD("references", function(sym){
+    if (sym instanceof AST_Symbol) sym = sym.definition();
+    return this.enclosed.indexOf(sym) < 0 ? null : sym;
+});
+
+AST_Symbol.DEFMETHOD("unmangleable", function(options){
+    return this.definition().unmangleable(options);
+});
+
+// property accessors are not mangleable
+AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
+    return true;
+});
+
+// labels are always mangleable
+AST_Label.DEFMETHOD("unmangleable", function(){
+    return false;
+});
+
+AST_Symbol.DEFMETHOD("unreferenced", function(){
+    return this.definition().references.length == 0
+        && !(this.scope.uses_eval || this.scope.uses_with);
+});
+
+AST_Symbol.DEFMETHOD("undeclared", function(){
+    return this.definition().undeclared;
+});
+
+AST_LabelRef.DEFMETHOD("undeclared", function(){
+    return false;
+});
+
+AST_Label.DEFMETHOD("undeclared", function(){
+    return false;
+});
+
+AST_Symbol.DEFMETHOD("definition", function(){
+    return this.thedef;
+});
+
+AST_Symbol.DEFMETHOD("global", function(){
+    return this.definition().global;
+});
+
+AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
+    return defaults(options, {
+        except      : [],
+        eval        : false,
+        sort        : false,
+        toplevel    : false,
+        screw_ie8   : false,
+        keep_fnames : false
+    });
+});
+
+AST_Toplevel.DEFMETHOD("mangle_names", function(options){
+    options = this._default_mangler_options(options);
+    // We only need to mangle declaration nodes.  Special logic wired
+    // into the code generator will display the mangled name if it's
+    // present (and for AST_SymbolRef-s it'll use the mangled name of
+    // the AST_SymbolDeclaration that it points to).
+    var lname = -1;
+    var to_mangle = [];
+
+    if (options.cache) {
+        this.globals.each(function(symbol){
+            if (options.except.indexOf(symbol.name) < 0) {
+                to_mangle.push(symbol);
+            }
+        });
+    }
+
+    var tw = new TreeWalker(function(node, descend){
+        if (node instanceof AST_LabeledStatement) {
+            // lname is incremented when we get to the AST_Label
+            var save_nesting = lname;
+            descend();
+            lname = save_nesting;
+            return true;        // don't descend again in TreeWalker
+        }
+        if (node instanceof AST_Scope) {
+            var p = tw.parent(), a = [];
+            node.variables.each(function(symbol){
+                if (options.except.indexOf(symbol.name) < 0) {
+                    a.push(symbol);
+                }
+            });
+            if (options.sort) a.sort(function(a, b){
+                return b.references.length - a.references.length;
+            });
+            to_mangle.push.apply(to_mangle, a);
+            return;
+        }
+        if (node instanceof AST_Label) {
+            var name;
+            do name = base54(++lname); while (!is_identifier(name));
+            node.mangled_name = name;
+            return true;
+        }
+        if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
+            to_mangle.push(node.definition());
+            return;
+        }
+    });
+    this.walk(tw);
+    to_mangle.forEach(function(def){ def.mangle(options) });
+
+    if (options.cache) {
+        options.cache.cname = this.cname;
+    }
+});
+
+AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
+    options = this._default_mangler_options(options);
+    var tw = new TreeWalker(function(node){
+        if (node instanceof AST_Constant)
+            base54.consider(node.print_to_string());
+        else if (node instanceof AST_Return)
+            base54.consider("return");
+        else if (node instanceof AST_Throw)
+            base54.consider("throw");
+        else if (node instanceof AST_Continue)
+            base54.consider("continue");
+        else if (node instanceof AST_Break)
+            base54.consider("break");
+        else if (node instanceof AST_Debugger)
+            base54.consider("debugger");
+        else if (node instanceof AST_Directive)
+            base54.consider(node.value);
+        else if (node instanceof AST_While)
+            base54.consider("while");
+        else if (node instanceof AST_Do)
+            base54.consider("do while");
+        else if (node instanceof AST_If) {
+            base54.consider("if");
+            if (node.alternative) base54.consider("else");
+        }
+        else if (node instanceof AST_Var)
+            base54.consider("var");
+        else if (node instanceof AST_Const)
+            base54.consider("const");
+        else if (node instanceof AST_Lambda)
+            base54.consider("function");
+        else if (node instanceof AST_For)
+            base54.consider("for");
+        else if (node instanceof AST_ForIn)
+            base54.consider("for in");
+        else if (node instanceof AST_Switch)
+            base54.consider("switch");
+        else if (node instanceof AST_Case)
+            base54.consider("case");
+        else if (node instanceof AST_Default)
+            base54.consider("default");
+        else if (node instanceof AST_With)
+            base54.consider("with");
+        else if (node instanceof AST_ObjectSetter)
+            base54.consider("set" + node.key);
+        else if (node instanceof AST_ObjectGetter)
+            base54.consider("get" + node.key);
+        else if (node instanceof AST_ObjectKeyVal)
+            base54.consider(node.key);
+        else if (node instanceof AST_New)
+            base54.consider("new");
+        else if (node instanceof AST_This)
+            base54.consider("this");
+        else if (node instanceof AST_Try)
+            base54.consider("try");
+        else if (node instanceof AST_Catch)
+            base54.consider("catch");
+        else if (node instanceof AST_Finally)
+            base54.consider("finally");
+        else if (node instanceof AST_Symbol && node.unmangleable(options))
+            base54.consider(node.name);
+        else if (node instanceof AST_Unary || node instanceof AST_Binary)
+            base54.consider(node.operator);
+        else if (node instanceof AST_Dot)
+            base54.consider(node.property);
+    });
+    this.walk(tw);
+    base54.sort();
+});
+
+var base54 = (function() {
+    var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
+    var chars, frequency;
+    function reset() {
+        frequency = Object.create(null);
+        chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
+        chars.forEach(function(ch){ frequency[ch] = 0 });
+    }
+    base54.consider = function(str){
+        for (var i = str.length; --i >= 0;) {
+            var code = str.charCodeAt(i);
+            if (code in frequency) ++frequency[code];
+        }
+    };
+    base54.sort = function() {
+        chars = mergeSort(chars, function(a, b){
+            if (is_digit(a) && !is_digit(b)) return 1;
+            if (is_digit(b) && !is_digit(a)) return -1;
+            return frequency[b] - frequency[a];
+        });
+    };
+    base54.reset = reset;
+    reset();
+    base54.get = function(){ return chars };
+    base54.freq = function(){ return frequency };
+    function base54(num) {
+        var ret = "", base = 54;
+        num++;
+        do {
+            num--;
+            ret += String.fromCharCode(chars[num % base]);
+            num = Math.floor(num / base);
+            base = 64;
+        } while (num > 0);
+        return ret;
+    };
+    return base54;
+})();
+
+AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
+    options = defaults(options, {
+        undeclared       : false, // this makes a lot of noise
+        unreferenced     : true,
+        assign_to_global : true,
+        func_arguments   : true,
+        nested_defuns    : true,
+        eval             : true
+    });
+    var tw = new TreeWalker(function(node){
+        if (options.undeclared
+            && node instanceof AST_SymbolRef
+            && node.undeclared())
+        {
+            // XXX: this also warns about JS standard names,
+            // i.e. Object, Array, parseInt etc.  Should add a list of
+            // exceptions.
+            AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", {
+                name: node.name,
+                file: node.start.file,
+                line: node.start.line,
+                col: node.start.col
+            });
+        }
+        if (options.assign_to_global)
+        {
+            var sym = null;
+            if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef)
+                sym = node.left;
+            else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef)
+                sym = node.init;
+            if (sym
+                && (sym.undeclared()
+                    || (sym.global() && sym.scope !== sym.definition().scope))) {
+                AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", {
+                    msg: sym.undeclared() ? "Accidental global?" : "Assignment to global",
+                    name: sym.name,
+                    file: sym.start.file,
+                    line: sym.start.line,
+                    col: sym.start.col
+                });
+            }
+        }
+        if (options.eval
+            && node instanceof AST_SymbolRef
+            && node.undeclared()
+            && node.name == "eval") {
+            AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start);
+        }
+        if (options.unreferenced
+            && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
+            && !(node instanceof AST_SymbolCatch)
+            && node.unreferenced()) {
+            AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
+                type: node instanceof AST_Label ? "Label" : "Symbol",
+                name: node.name,
+                file: node.start.file,
+                line: node.start.line,
+                col: node.start.col
+            });
+        }
+        if (options.func_arguments
+            && node instanceof AST_Lambda
+            && node.uses_arguments) {
+            AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", {
+                name: node.name ? node.name.name : "anonymous",
+                file: node.start.file,
+                line: node.start.line,
+                col: node.start.col
+            });
+        }
+        if (options.nested_defuns
+            && node instanceof AST_Defun
+            && !(tw.parent() instanceof AST_Scope)) {
+            AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", {
+                name: node.name.name,
+                type: tw.parent().TYPE,
+                file: node.start.file,
+                line: node.start.line,
+                col: node.start.col
+            });
+        }
+    });
+    this.walk(tw);
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/sourcemap.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/sourcemap.js
new file mode 100644
index 0000000..a67011f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/sourcemap.js
@@ -0,0 +1,92 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+// a small wrapper around fitzgen's source-map library
+function SourceMap(options) {
+    options = defaults(options, {
+        file : null,
+        root : null,
+        orig : null,
+
+        orig_line_diff : 0,
+        dest_line_diff : 0,
+    });
+    var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
+    var generator;
+    if (orig_map) {
+      generator = MOZ_SourceMap.SourceMapGenerator.fromSourceMap(orig_map);
+    } else {
+        generator = new MOZ_SourceMap.SourceMapGenerator({
+            file       : options.file,
+            sourceRoot : options.root
+        });
+    }
+    function add(source, gen_line, gen_col, orig_line, orig_col, name) {
+        if (orig_map) {
+            var info = orig_map.originalPositionFor({
+                line: orig_line,
+                column: orig_col
+            });
+            if (info.source === null) {
+                return;
+            }
+            source = info.source;
+            orig_line = info.line;
+            orig_col = info.column;
+            name = info.name || name;
+        }
+        generator.addMapping({
+            generated : { line: gen_line + options.dest_line_diff, column: gen_col },
+            original  : { line: orig_line + options.orig_line_diff, column: orig_col },
+            source    : source,
+            name      : name
+        });
+    }
+    return {
+        add        : add,
+        get        : function() { return generator },
+        toString   : function() { return JSON.stringify(generator.toJSON()); }
+    };
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/transform.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/transform.js
new file mode 100644
index 0000000..c3c34f5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/transform.js
@@ -0,0 +1,218 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+// Tree transformer helpers.
+
+function TreeTransformer(before, after) {
+    TreeWalker.call(this);
+    this.before = before;
+    this.after = after;
+}
+TreeTransformer.prototype = new TreeWalker;
+
+(function(undefined){
+
+    function _(node, descend) {
+        node.DEFMETHOD("transform", function(tw, in_list){
+            var x, y;
+            tw.push(this);
+            if (tw.before) x = tw.before(this, descend, in_list);
+            if (x === undefined) {
+                if (!tw.after) {
+                    x = this;
+                    descend(x, tw);
+                } else {
+                    tw.stack[tw.stack.length - 1] = x = this.clone();
+                    descend(x, tw);
+                    y = tw.after(x, in_list);
+                    if (y !== undefined) x = y;
+                }
+            }
+            tw.pop();
+            return x;
+        });
+    };
+
+    function do_list(list, tw) {
+        return MAP(list, function(node){
+            return node.transform(tw, true);
+        });
+    };
+
+    _(AST_Node, noop);
+
+    _(AST_LabeledStatement, function(self, tw){
+        self.label = self.label.transform(tw);
+        self.body = self.body.transform(tw);
+    });
+
+    _(AST_SimpleStatement, function(self, tw){
+        self.body = self.body.transform(tw);
+    });
+
+    _(AST_Block, function(self, tw){
+        self.body = do_list(self.body, tw);
+    });
+
+    _(AST_DWLoop, function(self, tw){
+        self.condition = self.condition.transform(tw);
+        self.body = self.body.transform(tw);
+    });
+
+    _(AST_For, function(self, tw){
+        if (self.init) self.init = self.init.transform(tw);
+        if (self.condition) self.condition = self.condition.transform(tw);
+        if (self.step) self.step = self.step.transform(tw);
+        self.body = self.body.transform(tw);
+    });
+
+    _(AST_ForIn, function(self, tw){
+        self.init = self.init.transform(tw);
+        self.object = self.object.transform(tw);
+        self.body = self.body.transform(tw);
+    });
+
+    _(AST_With, function(self, tw){
+        self.expression = self.expression.transform(tw);
+        self.body = self.body.transform(tw);
+    });
+
+    _(AST_Exit, function(self, tw){
+        if (self.value) self.value = self.value.transform(tw);
+    });
+
+    _(AST_LoopControl, function(self, tw){
+        if (self.label) self.label = self.label.transform(tw);
+    });
+
+    _(AST_If, function(self, tw){
+        self.condition = self.condition.transform(tw);
+        self.body = self.body.transform(tw);
+        if (self.alternative) self.alternative = self.alternative.transform(tw);
+    });
+
+    _(AST_Switch, function(self, tw){
+        self.expression = self.expression.transform(tw);
+        self.body = do_list(self.body, tw);
+    });
+
+    _(AST_Case, function(self, tw){
+        self.expression = self.expression.transform(tw);
+        self.body = do_list(self.body, tw);
+    });
+
+    _(AST_Try, function(self, tw){
+        self.body = do_list(self.body, tw);
+        if (self.bcatch) self.bcatch = self.bcatch.transform(tw);
+        if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
+    });
+
+    _(AST_Catch, function(self, tw){
+        self.argname = self.argname.transform(tw);
+        self.body = do_list(self.body, tw);
+    });
+
+    _(AST_Definitions, function(self, tw){
+        self.definitions = do_list(self.definitions, tw);
+    });
+
+    _(AST_VarDef, function(self, tw){
+        self.name = self.name.transform(tw);
+        if (self.value) self.value = self.value.transform(tw);
+    });
+
+    _(AST_Lambda, function(self, tw){
+        if (self.name) self.name = self.name.transform(tw);
+        self.argnames = do_list(self.argnames, tw);
+        self.body = do_list(self.body, tw);
+    });
+
+    _(AST_Call, function(self, tw){
+        self.expression = self.expression.transform(tw);
+        self.args = do_list(self.args, tw);
+    });
+
+    _(AST_Seq, function(self, tw){
+        self.car = self.car.transform(tw);
+        self.cdr = self.cdr.transform(tw);
+    });
+
+    _(AST_Dot, function(self, tw){
+        self.expression = self.expression.transform(tw);
+    });
+
+    _(AST_Sub, function(self, tw){
+        self.expression = self.expression.transform(tw);
+        self.property = self.property.transform(tw);
+    });
+
+    _(AST_Unary, function(self, tw){
+        self.expression = self.expression.transform(tw);
+    });
+
+    _(AST_Binary, function(self, tw){
+        self.left = self.left.transform(tw);
+        self.right = self.right.transform(tw);
+    });
+
+    _(AST_Conditional, function(self, tw){
+        self.condition = self.condition.transform(tw);
+        self.consequent = self.consequent.transform(tw);
+        self.alternative = self.alternative.transform(tw);
+    });
+
+    _(AST_Array, function(self, tw){
+        self.elements = do_list(self.elements, tw);
+    });
+
+    _(AST_Object, function(self, tw){
+        self.properties = do_list(self.properties, tw);
+    });
+
+    _(AST_ObjectProperty, function(self, tw){
+        self.value = self.value.transform(tw);
+    });
+
+})();
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/utils.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/utils.js
new file mode 100644
index 0000000..4612a32
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/lib/utils.js
@@ -0,0 +1,310 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+  https://github.com/mishoo/UglifyJS2
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <mihai.bazon@gmail.com>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+"use strict";
+
+function array_to_hash(a) {
+    var ret = Object.create(null);
+    for (var i = 0; i < a.length; ++i)
+        ret[a[i]] = true;
+    return ret;
+};
+
+function slice(a, start) {
+    return Array.prototype.slice.call(a, start || 0);
+};
+
+function characters(str) {
+    return str.split("");
+};
+
+function member(name, array) {
+    for (var i = array.length; --i >= 0;)
+        if (array[i] == name)
+            return true;
+    return false;
+};
+
+function find_if(func, array) {
+    for (var i = 0, n = array.length; i < n; ++i) {
+        if (func(array[i]))
+            return array[i];
+    }
+};
+
+function repeat_string(str, i) {
+    if (i <= 0) return "";
+    if (i == 1) return str;
+    var d = repeat_string(str, i >> 1);
+    d += d;
+    if (i & 1) d += str;
+    return d;
+};
+
+function DefaultsError(msg, defs) {
+    Error.call(this, msg);
+    this.msg = msg;
+    this.defs = defs;
+};
+DefaultsError.prototype = Object.create(Error.prototype);
+DefaultsError.prototype.constructor = DefaultsError;
+
+DefaultsError.croak = function(msg, defs) {
+    throw new DefaultsError(msg, defs);
+};
+
+function defaults(args, defs, croak) {
+    if (args === true)
+        args = {};
+    var ret = args || {};
+    if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
+        DefaultsError.croak("`" + i + "` is not a supported option", defs);
+    for (var i in defs) if (defs.hasOwnProperty(i)) {
+        ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
+    }
+    return ret;
+};
+
+function merge(obj, ext) {
+    var count = 0;
+    for (var i in ext) if (ext.hasOwnProperty(i)) {
+        obj[i] = ext[i];
+        count++;
+    }
+    return count;
+};
+
+function noop() {};
+
+var MAP = (function(){
+    function MAP(a, f, backwards) {
+        var ret = [], top = [], i;
+        function doit() {
+            var val = f(a[i], i);
+            var is_last = val instanceof Last;
+            if (is_last) val = val.v;
+            if (val instanceof AtTop) {
+                val = val.v;
+                if (val instanceof Splice) {
+                    top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
+                } else {
+                    top.push(val);
+                }
+            }
+            else if (val !== skip) {
+                if (val instanceof Splice) {
+                    ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
+                } else {
+                    ret.push(val);
+                }
+            }
+            return is_last;
+        };
+        if (a instanceof Array) {
+            if (backwards) {
+                for (i = a.length; --i >= 0;) if (doit()) break;
+                ret.reverse();
+                top.reverse();
+            } else {
+                for (i = 0; i < a.length; ++i) if (doit()) break;
+            }
+        }
+        else {
+            for (i in a) if (a.hasOwnProperty(i)) if (doit()) break;
+        }
+        return top.concat(ret);
+    };
+    MAP.at_top = function(val) { return new AtTop(val) };
+    MAP.splice = function(val) { return new Splice(val) };
+    MAP.last = function(val) { return new Last(val) };
+    var skip = MAP.skip = {};
+    function AtTop(val) { this.v = val };
+    function Splice(val) { this.v = val };
+    function Last(val) { this.v = val };
+    return MAP;
+})();
+
+function push_uniq(array, el) {
+    if (array.indexOf(el) < 0)
+        array.push(el);
+};
+
+function string_template(text, props) {
+    return text.replace(/\{(.+?)\}/g, function(str, p){
+        return props[p];
+    });
+};
+
+function remove(array, el) {
+    for (var i = array.length; --i >= 0;) {
+        if (array[i] === el) array.splice(i, 1);
+    }
+};
+
+function mergeSort(array, cmp) {
+    if (array.length < 2) return array.slice();
+    function merge(a, b) {
+        var r = [], ai = 0, bi = 0, i = 0;
+        while (ai < a.length && bi < b.length) {
+            cmp(a[ai], b[bi]) <= 0
+                ? r[i++] = a[ai++]
+                : r[i++] = b[bi++];
+        }
+        if (ai < a.length) r.push.apply(r, a.slice(ai));
+        if (bi < b.length) r.push.apply(r, b.slice(bi));
+        return r;
+    };
+    function _ms(a) {
+        if (a.length <= 1)
+            return a;
+        var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
+        left = _ms(left);
+        right = _ms(right);
+        return merge(left, right);
+    };
+    return _ms(array);
+};
+
+function set_difference(a, b) {
+    return a.filter(function(el){
+        return b.indexOf(el) < 0;
+    });
+};
+
+function set_intersection(a, b) {
+    return a.filter(function(el){
+        return b.indexOf(el) >= 0;
+    });
+};
+
+// this function is taken from Acorn [1], written by Marijn Haverbeke
+// [1] https://github.com/marijnh/acorn
+function makePredicate(words) {
+    if (!(words instanceof Array)) words = words.split(" ");
+    var f = "", cats = [];
+    out: for (var i = 0; i < words.length; ++i) {
+        for (var j = 0; j < cats.length; ++j)
+            if (cats[j][0].length == words[i].length) {
+                cats[j].push(words[i]);
+                continue out;
+            }
+        cats.push([words[i]]);
+    }
+    function compareTo(arr) {
+        if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
+        f += "switch(str){";
+        for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
+        f += "return true}return false;";
+    }
+    // When there are more than three length categories, an outer
+    // switch first dispatches on the lengths, to save on comparisons.
+    if (cats.length > 3) {
+        cats.sort(function(a, b) {return b.length - a.length;});
+        f += "switch(str.length){";
+        for (var i = 0; i < cats.length; ++i) {
+            var cat = cats[i];
+            f += "case " + cat[0].length + ":";
+            compareTo(cat);
+        }
+        f += "}";
+        // Otherwise, simply generate a flat `switch` statement.
+    } else {
+        compareTo(words);
+    }
+    return new Function("str", f);
+};
+
+function all(array, predicate) {
+    for (var i = array.length; --i >= 0;)
+        if (!predicate(array[i]))
+            return false;
+    return true;
+};
+
+function Dictionary() {
+    this._values = Object.create(null);
+    this._size = 0;
+};
+Dictionary.prototype = {
+    set: function(key, val) {
+        if (!this.has(key)) ++this._size;
+        this._values["$" + key] = val;
+        return this;
+    },
+    add: function(key, val) {
+        if (this.has(key)) {
+            this.get(key).push(val);
+        } else {
+            this.set(key, [ val ]);
+        }
+        return this;
+    },
+    get: function(key) { return this._values["$" + key] },
+    del: function(key) {
+        if (this.has(key)) {
+            --this._size;
+            delete this._values["$" + key];
+        }
+        return this;
+    },
+    has: function(key) { return ("$" + key) in this._values },
+    each: function(f) {
+        for (var i in this._values)
+            f(this._values[i], i.substr(1));
+    },
+    size: function() {
+        return this._size;
+    },
+    map: function(f) {
+        var ret = [];
+        for (var i in this._values)
+            ret.push(f(this._values[i], i.substr(1)));
+        return ret;
+    },
+    toObject: function() { return this._values }
+};
+Dictionary.fromObject = function(obj) {
+    var dict = new Dictionary();
+    dict._size = merge(dict._values, obj);
+    return dict;
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/LICENSE
new file mode 100644
index 0000000..b7f9d50
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Caolan McMahon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/README.md
new file mode 100644
index 0000000..951f76e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/README.md
@@ -0,0 +1,1425 @@
+# Async.js
+
+Async is a utility module which provides straight-forward, powerful functions
+for working with asynchronous JavaScript. Although originally designed for
+use with [node.js](http://nodejs.org), it can also be used directly in the
+browser. Also supports [component](https://github.com/component/component).
+
+Async provides around 20 functions that include the usual 'functional'
+suspects (map, reduce, filter, each…) as well as some common patterns
+for asynchronous control flow (parallel, series, waterfall…). All these
+functions assume you follow the node.js convention of providing a single
+callback as the last argument of your async function.
+
+
+## Quick Examples
+
+```javascript
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+    // results is now an array of stats for each file
+});
+
+async.filter(['file1','file2','file3'], fs.exists, function(results){
+    // results now equals an array of the existing files
+});
+
+async.parallel([
+    function(){ ... },
+    function(){ ... }
+], callback);
+
+async.series([
+    function(){ ... },
+    function(){ ... }
+]);
+```
+
+There are many more functions available so take a look at the docs below for a
+full list. This module aims to be comprehensive, so if you feel anything is
+missing please create a GitHub issue for it.
+
+## Common Pitfalls
+
+### Binding a context to an iterator
+
+This section is really about bind, not about async. If you are wondering how to
+make async execute your iterators in a given context, or are confused as to why
+a method of another library isn't working as an iterator, study this example:
+
+```js
+// Here is a simple object with an (unnecessarily roundabout) squaring method
+var AsyncSquaringLibrary = {
+  squareExponent: 2,
+  square: function(number, callback){ 
+    var result = Math.pow(number, this.squareExponent);
+    setTimeout(function(){
+      callback(null, result);
+    }, 200);
+  }
+};
+
+async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){
+  // result is [NaN, NaN, NaN]
+  // This fails because the `this.squareExponent` expression in the square
+  // function is not evaluated in the context of AsyncSquaringLibrary, and is
+  // therefore undefined.
+});
+
+async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){
+  // result is [1, 4, 9]
+  // With the help of bind we can attach a context to the iterator before
+  // passing it to async. Now the square function will be executed in its 
+  // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`
+  // will be as expected.
+});
+```
+
+## Download
+
+The source is available for download from
+[GitHub](http://github.com/caolan/async).
+Alternatively, you can install using Node Package Manager (npm):
+
+    npm install async
+
+__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed
+
+## In the Browser
+
+So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
+
+```html
+<script type="text/javascript" src="async.js"></script>
+<script type="text/javascript">
+
+    async.map(data, asyncProcess, function(err, results){
+        alert(results);
+    });
+
+</script>
+```
+
+## Documentation
+
+### Collections
+
+* [each](#each)
+* [eachSeries](#eachSeries)
+* [eachLimit](#eachLimit)
+* [map](#map)
+* [mapSeries](#mapSeries)
+* [mapLimit](#mapLimit)
+* [filter](#filter)
+* [filterSeries](#filterSeries)
+* [reject](#reject)
+* [rejectSeries](#rejectSeries)
+* [reduce](#reduce)
+* [reduceRight](#reduceRight)
+* [detect](#detect)
+* [detectSeries](#detectSeries)
+* [sortBy](#sortBy)
+* [some](#some)
+* [every](#every)
+* [concat](#concat)
+* [concatSeries](#concatSeries)
+
+### Control Flow
+
+* [series](#series)
+* [parallel](#parallel)
+* [parallelLimit](#parallellimittasks-limit-callback)
+* [whilst](#whilst)
+* [doWhilst](#doWhilst)
+* [until](#until)
+* [doUntil](#doUntil)
+* [forever](#forever)
+* [waterfall](#waterfall)
+* [compose](#compose)
+* [applyEach](#applyEach)
+* [applyEachSeries](#applyEachSeries)
+* [queue](#queue)
+* [cargo](#cargo)
+* [auto](#auto)
+* [iterator](#iterator)
+* [apply](#apply)
+* [nextTick](#nextTick)
+* [times](#times)
+* [timesSeries](#timesSeries)
+
+### Utils
+
+* [memoize](#memoize)
+* [unmemoize](#unmemoize)
+* [log](#log)
+* [dir](#dir)
+* [noConflict](#noConflict)
+
+
+## Collections
+
+<a name="forEach" />
+<a name="each" />
+### each(arr, iterator, callback)
+
+Applies an iterator function to each item in an array, in parallel.
+The iterator is called with an item from the list and a callback for when it
+has finished. If the iterator passes an error to this callback, the main
+callback for the each function is immediately called with the error.
+
+Note, that since this function applies the iterator to each item in parallel
+there is no guarantee that the iterator functions will complete in order.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A function to apply to each item in the array.
+  The iterator is passed a callback(err) which must be called once it has 
+  completed. If no error has occured, the callback should be run without 
+  arguments or with an explicit null argument.
+* callback(err) - A callback which is called after all the iterator functions
+  have finished, or an error has occurred.
+
+__Example__
+
+```js
+// assuming openFiles is an array of file names and saveFile is a function
+// to save the modified contents of that file:
+
+async.each(openFiles, saveFile, function(err){
+    // if any of the saves produced an error, err would equal that error
+});
+```
+
+---------------------------------------
+
+<a name="forEachSeries" />
+<a name="eachSeries" />
+### eachSeries(arr, iterator, callback)
+
+The same as each only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. This means the iterator functions will complete in order.
+
+
+---------------------------------------
+
+<a name="forEachLimit" />
+<a name="eachLimit" />
+### eachLimit(arr, limit, iterator, callback)
+
+The same as each only no more than "limit" iterators will be simultaneously 
+running at any time.
+
+Note that the items are not processed in batches, so there is no guarantee that
+ the first "limit" iterator functions will complete before any others are 
+started.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* limit - The maximum number of iterators to run at any time.
+* iterator(item, callback) - A function to apply to each item in the array.
+  The iterator is passed a callback(err) which must be called once it has 
+  completed. If no error has occured, the callback should be run without 
+  arguments or with an explicit null argument.
+* callback(err) - A callback which is called after all the iterator functions
+  have finished, or an error has occurred.
+
+__Example__
+
+```js
+// Assume documents is an array of JSON objects and requestApi is a
+// function that interacts with a rate-limited REST api.
+
+async.eachLimit(documents, 20, requestApi, function(err){
+    // if any of the saves produced an error, err would equal that error
+});
+```
+
+---------------------------------------
+
+<a name="map" />
+### map(arr, iterator, callback)
+
+Produces a new array of values by mapping each value in the given array through
+the iterator function. The iterator is called with an item from the array and a
+callback for when it has finished processing. The callback takes 2 arguments, 
+an error and the transformed item from the array. If the iterator passes an
+error to this callback, the main callback for the map function is immediately
+called with the error.
+
+Note, that since this function applies the iterator to each item in parallel
+there is no guarantee that the iterator functions will complete in order, however
+the results array will be in the same order as the original array.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A function to apply to each item in the array.
+  The iterator is passed a callback(err, transformed) which must be called once 
+  it has completed with an error (which can be null) and a transformed item.
+* callback(err, results) - A callback which is called after all the iterator
+  functions have finished, or an error has occurred. Results is an array of the
+  transformed items from the original array.
+
+__Example__
+
+```js
+async.map(['file1','file2','file3'], fs.stat, function(err, results){
+    // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
+<a name="mapSeries" />
+### mapSeries(arr, iterator, callback)
+
+The same as map only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. The results array will be in the same order as the original.
+
+
+---------------------------------------
+
+<a name="mapLimit" />
+### mapLimit(arr, limit, iterator, callback)
+
+The same as map only no more than "limit" iterators will be simultaneously 
+running at any time.
+
+Note that the items are not processed in batches, so there is no guarantee that
+ the first "limit" iterator functions will complete before any others are 
+started.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* limit - The maximum number of iterators to run at any time.
+* iterator(item, callback) - A function to apply to each item in the array.
+  The iterator is passed a callback(err, transformed) which must be called once 
+  it has completed with an error (which can be null) and a transformed item.
+* callback(err, results) - A callback which is called after all the iterator
+  functions have finished, or an error has occurred. Results is an array of the
+  transformed items from the original array.
+
+__Example__
+
+```js
+async.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
+    // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
+<a name="filter" />
+### filter(arr, iterator, callback)
+
+__Alias:__ select
+
+Returns a new array of all the values which pass an async truth test.
+_The callback for each iterator call only accepts a single argument of true or
+false, it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like fs.exists. This operation is
+performed in parallel, but the results array will be in the same order as the
+original.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+  The iterator is passed a callback(truthValue) which must be called with a 
+  boolean argument once it has completed.
+* callback(results) - A callback which is called after all the iterator
+  functions have finished.
+
+__Example__
+
+```js
+async.filter(['file1','file2','file3'], fs.exists, function(results){
+    // results now equals an array of the existing files
+});
+```
+
+---------------------------------------
+
+<a name="filterSeries" />
+### filterSeries(arr, iterator, callback)
+
+__alias:__ selectSeries
+
+The same as filter only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. The results array will be in the same order as the original.
+
+---------------------------------------
+
+<a name="reject" />
+### reject(arr, iterator, callback)
+
+The opposite of filter. Removes values that pass an async truth test.
+
+---------------------------------------
+
+<a name="rejectSeries" />
+### rejectSeries(arr, iterator, callback)
+
+The same as reject, only the iterator is applied to each item in the array
+in series.
+
+
+---------------------------------------
+
+<a name="reduce" />
+### reduce(arr, memo, iterator, callback)
+
+__aliases:__ inject, foldl
+
+Reduces a list of values into a single value using an async iterator to return
+each successive step. Memo is the initial state of the reduction. This
+function only operates in series. For performance reasons, it may make sense to
+split a call to this function into a parallel map, then use the normal
+Array.prototype.reduce on the results. This function is for situations where
+each step in the reduction needs to be async, if you can get the data before
+reducing it then it's probably a good idea to do so.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* memo - The initial state of the reduction.
+* iterator(memo, item, callback) - A function applied to each item in the
+  array to produce the next step in the reduction. The iterator is passed a
+  callback(err, reduction) which accepts an optional error as its first 
+  argument, and the state of the reduction as the second. If an error is 
+  passed to the callback, the reduction is stopped and the main callback is 
+  immediately called with the error.
+* callback(err, result) - A callback which is called after all the iterator
+  functions have finished. Result is the reduced value.
+
+__Example__
+
+```js
+async.reduce([1,2,3], 0, function(memo, item, callback){
+    // pointless async:
+    process.nextTick(function(){
+        callback(null, memo + item)
+    });
+}, function(err, result){
+    // result is now equal to the last value of memo, which is 6
+});
+```
+
+---------------------------------------
+
+<a name="reduceRight" />
+### reduceRight(arr, memo, iterator, callback)
+
+__Alias:__ foldr
+
+Same as reduce, only operates on the items in the array in reverse order.
+
+
+---------------------------------------
+
+<a name="detect" />
+### detect(arr, iterator, callback)
+
+Returns the first value in a list that passes an async truth test. The
+iterator is applied in parallel, meaning the first iterator to return true will
+fire the detect callback with that result. That means the result might not be
+the first item in the original array (in terms of order) that passes the test.
+
+If order within the original array is important then look at detectSeries.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+  The iterator is passed a callback(truthValue) which must be called with a 
+  boolean argument once it has completed.
+* callback(result) - A callback which is called as soon as any iterator returns
+  true, or after all the iterator functions have finished. Result will be
+  the first item in the array that passes the truth test (iterator) or the
+  value undefined if none passed.
+
+__Example__
+
+```js
+async.detect(['file1','file2','file3'], fs.exists, function(result){
+    // result now equals the first file in the list that exists
+});
+```
+
+---------------------------------------
+
+<a name="detectSeries" />
+### detectSeries(arr, iterator, callback)
+
+The same as detect, only the iterator is applied to each item in the array
+in series. This means the result is always the first in the original array (in
+terms of array order) that passes the truth test.
+
+
+---------------------------------------
+
+<a name="sortBy" />
+### sortBy(arr, iterator, callback)
+
+Sorts a list by the results of running each value through an async iterator.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A function to apply to each item in the array.
+  The iterator is passed a callback(err, sortValue) which must be called once it
+  has completed with an error (which can be null) and a value to use as the sort
+  criteria.
+* callback(err, results) - A callback which is called after all the iterator
+  functions have finished, or an error has occurred. Results is the items from
+  the original array sorted by the values returned by the iterator calls.
+
+__Example__
+
+```js
+async.sortBy(['file1','file2','file3'], function(file, callback){
+    fs.stat(file, function(err, stats){
+        callback(err, stats.mtime);
+    });
+}, function(err, results){
+    // results is now the original array of files sorted by
+    // modified date
+});
+```
+
+---------------------------------------
+
+<a name="some" />
+### some(arr, iterator, callback)
+
+__Alias:__ any
+
+Returns true if at least one element in the array satisfies an async test.
+_The callback for each iterator call only accepts a single argument of true or
+false, it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like fs.exists. Once any iterator
+call returns true, the main callback is immediately called.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+  The iterator is passed a callback(truthValue) which must be called with a 
+  boolean argument once it has completed.
+* callback(result) - A callback which is called as soon as any iterator returns
+  true, or after all the iterator functions have finished. Result will be
+  either true or false depending on the values of the async tests.
+
+__Example__
+
+```js
+async.some(['file1','file2','file3'], fs.exists, function(result){
+    // if result is true then at least one of the files exists
+});
+```
+
+---------------------------------------
+
+<a name="every" />
+### every(arr, iterator, callback)
+
+__Alias:__ all
+
+Returns true if every element in the array satisfies an async test.
+_The callback for each iterator call only accepts a single argument of true or
+false, it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like fs.exists.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, callback) - A truth test to apply to each item in the array.
+  The iterator is passed a callback(truthValue) which must be called with a 
+  boolean argument once it has completed.
+* callback(result) - A callback which is called after all the iterator
+  functions have finished. Result will be either true or false depending on
+  the values of the async tests.
+
+__Example__
+
+```js
+async.every(['file1','file2','file3'], fs.exists, function(result){
+    // if result is true then every file exists
+});
+```
+
+---------------------------------------
+
+<a name="concat" />
+### concat(arr, iterator, callback)
+
+Applies an iterator to each item in a list, concatenating the results. Returns the
+concatenated list. The iterators are called in parallel, and the results are
+concatenated as they return. There is no guarantee that the results array will
+be returned in the original order of the arguments passed to the iterator function.
+
+__Arguments__
+
+* arr - An array to iterate over
+* iterator(item, callback) - A function to apply to each item in the array.
+  The iterator is passed a callback(err, results) which must be called once it 
+  has completed with an error (which can be null) and an array of results.
+* callback(err, results) - A callback which is called after all the iterator
+  functions have finished, or an error has occurred. Results is an array containing
+  the concatenated results of the iterator function.
+
+__Example__
+
+```js
+async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
+    // files is now a list of filenames that exist in the 3 directories
+});
+```
+
+---------------------------------------
+
+<a name="concatSeries" />
+### concatSeries(arr, iterator, callback)
+
+Same as async.concat, but executes in series instead of parallel.
+
+
+## Control Flow
+
+<a name="series" />
+### series(tasks, [callback])
+
+Run an array of functions in series, each one running once the previous
+function has completed. If any functions in the series pass an error to its
+callback, no more functions are run and the callback for the series is
+immediately called with the value of the error. Once the tasks have completed,
+the results are passed to the final callback as an array.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function and the results will be passed to the final callback as an object
+instead of an array. This can be a more readable way of handling results from
+async.series.
+
+
+__Arguments__
+
+* tasks - An array or object containing functions to run, each function is passed
+  a callback(err, result) it must call on completion with an error (which can
+  be null) and an optional result value.
+* callback(err, results) - An optional callback to run once all the functions
+  have completed. This function gets a results array (or object) containing all 
+  the result arguments passed to the task callbacks.
+
+__Example__
+
+```js
+async.series([
+    function(callback){
+        // do some stuff ...
+        callback(null, 'one');
+    },
+    function(callback){
+        // do some more stuff ...
+        callback(null, 'two');
+    }
+],
+// optional callback
+function(err, results){
+    // results is now equal to ['one', 'two']
+});
+
+
+// an example using an object instead of an array
+async.series({
+    one: function(callback){
+        setTimeout(function(){
+            callback(null, 1);
+        }, 200);
+    },
+    two: function(callback){
+        setTimeout(function(){
+            callback(null, 2);
+        }, 100);
+    }
+},
+function(err, results) {
+    // results is now equal to: {one: 1, two: 2}
+});
+```
+
+---------------------------------------
+
+<a name="parallel" />
+### parallel(tasks, [callback])
+
+Run an array of functions in parallel, without waiting until the previous
+function has completed. If any of the functions pass an error to its
+callback, the main callback is immediately called with the value of the error.
+Once the tasks have completed, the results are passed to the final callback as an
+array.
+
+It is also possible to use an object instead of an array. Each property will be
+run as a function and the results will be passed to the final callback as an object
+instead of an array. This can be a more readable way of handling results from
+async.parallel.
+
+
+__Arguments__
+
+* tasks - An array or object containing functions to run, each function is passed 
+  a callback(err, result) it must call on completion with an error (which can
+  be null) and an optional result value.
+* callback(err, results) - An optional callback to run once all the functions
+  have completed. This function gets a results array (or object) containing all 
+  the result arguments passed to the task callbacks.
+
+__Example__
+
+```js
+async.parallel([
+    function(callback){
+        setTimeout(function(){
+            callback(null, 'one');
+        }, 200);
+    },
+    function(callback){
+        setTimeout(function(){
+            callback(null, 'two');
+        }, 100);
+    }
+],
+// optional callback
+function(err, results){
+    // the results array will equal ['one','two'] even though
+    // the second function had a shorter timeout.
+});
+
+
+// an example using an object instead of an array
+async.parallel({
+    one: function(callback){
+        setTimeout(function(){
+            callback(null, 1);
+        }, 200);
+    },
+    two: function(callback){
+        setTimeout(function(){
+            callback(null, 2);
+        }, 100);
+    }
+},
+function(err, results) {
+    // results is now equals to: {one: 1, two: 2}
+});
+```
+
+---------------------------------------
+
+<a name="parallel" />
+### parallelLimit(tasks, limit, [callback])
+
+The same as parallel only the tasks are executed in parallel with a maximum of "limit" 
+tasks executing at any time.
+
+Note that the tasks are not executed in batches, so there is no guarantee that 
+the first "limit" tasks will complete before any others are started.
+
+__Arguments__
+
+* tasks - An array or object containing functions to run, each function is passed 
+  a callback(err, result) it must call on completion with an error (which can
+  be null) and an optional result value.
+* limit - The maximum number of tasks to run at any time.
+* callback(err, results) - An optional callback to run once all the functions
+  have completed. This function gets a results array (or object) containing all 
+  the result arguments passed to the task callbacks.
+
+---------------------------------------
+
+<a name="whilst" />
+### whilst(test, fn, callback)
+
+Repeatedly call fn, while test returns true. Calls the callback when stopped,
+or an error occurs.
+
+__Arguments__
+
+* test() - synchronous truth test to perform before each execution of fn.
+* fn(callback) - A function to call each time the test passes. The function is
+  passed a callback(err) which must be called once it has completed with an 
+  optional error argument.
+* callback(err) - A callback which is called after the test fails and repeated
+  execution of fn has stopped.
+
+__Example__
+
+```js
+var count = 0;
+
+async.whilst(
+    function () { return count < 5; },
+    function (callback) {
+        count++;
+        setTimeout(callback, 1000);
+    },
+    function (err) {
+        // 5 seconds have passed
+    }
+);
+```
+
+---------------------------------------
+
+<a name="doWhilst" />
+### doWhilst(fn, test, callback)
+
+The post check version of whilst. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
+
+---------------------------------------
+
+<a name="until" />
+### until(test, fn, callback)
+
+Repeatedly call fn, until test returns true. Calls the callback when stopped,
+or an error occurs.
+
+The inverse of async.whilst.
+
+---------------------------------------
+
+<a name="doUntil" />
+### doUntil(fn, test, callback)
+
+Like doWhilst except the test is inverted. Note the argument ordering differs from `until`.
+
+---------------------------------------
+
+<a name="forever" />
+### forever(fn, callback)
+
+Calls the asynchronous function 'fn' repeatedly, in series, indefinitely.
+If an error is passed to fn's callback then 'callback' is called with the
+error, otherwise it will never be called.
+
+---------------------------------------
+
+<a name="waterfall" />
+### waterfall(tasks, [callback])
+
+Runs an array of functions in series, each passing their results to the next in
+the array. However, if any of the functions pass an error to the callback, the
+next function is not executed and the main callback is immediately called with
+the error.
+
+__Arguments__
+
+* tasks - An array of functions to run, each function is passed a 
+  callback(err, result1, result2, ...) it must call on completion. The first
+  argument is an error (which can be null) and any further arguments will be 
+  passed as arguments in order to the next task.
+* callback(err, [results]) - An optional callback to run once all the functions
+  have completed. This will be passed the results of the last task's callback.
+
+
+
+__Example__
+
+```js
+async.waterfall([
+    function(callback){
+        callback(null, 'one', 'two');
+    },
+    function(arg1, arg2, callback){
+        callback(null, 'three');
+    },
+    function(arg1, callback){
+        // arg1 now equals 'three'
+        callback(null, 'done');
+    }
+], function (err, result) {
+   // result now equals 'done'    
+});
+```
+
+---------------------------------------
+<a name="compose" />
+### compose(fn1, fn2...)
+
+Creates a function which is a composition of the passed asynchronous
+functions. Each function consumes the return value of the function that
+follows. Composing functions f(), g() and h() would produce the result of
+f(g(h())), only this version uses callbacks to obtain the return values.
+
+Each function is executed with the `this` binding of the composed function.
+
+__Arguments__
+
+* functions... - the asynchronous functions to compose
+
+
+__Example__
+
+```js
+function add1(n, callback) {
+    setTimeout(function () {
+        callback(null, n + 1);
+    }, 10);
+}
+
+function mul3(n, callback) {
+    setTimeout(function () {
+        callback(null, n * 3);
+    }, 10);
+}
+
+var add1mul3 = async.compose(mul3, add1);
+
+add1mul3(4, function (err, result) {
+   // result now equals 15
+});
+```
+
+---------------------------------------
+<a name="applyEach" />
+### applyEach(fns, args..., callback)
+
+Applies the provided arguments to each function in the array, calling the
+callback after all functions have completed. If you only provide the first
+argument then it will return a function which lets you pass in the
+arguments as if it were a single function call.
+
+__Arguments__
+
+* fns - the asynchronous functions to all call with the same arguments
+* args... - any number of separate arguments to pass to the function
+* callback - the final argument should be the callback, called when all
+  functions have completed processing
+
+
+__Example__
+
+```js
+async.applyEach([enableSearch, updateSchema], 'bucket', callback);
+
+// partial application example:
+async.each(
+    buckets,
+    async.applyEach([enableSearch, updateSchema]),
+    callback
+);
+```
+
+---------------------------------------
+
+<a name="applyEachSeries" />
+### applyEachSeries(arr, iterator, callback)
+
+The same as applyEach only the functions are applied in series.
+
+---------------------------------------
+
+<a name="queue" />
+### queue(worker, concurrency)
+
+Creates a queue object with the specified concurrency. Tasks added to the
+queue will be processed in parallel (up to the concurrency limit). If all
+workers are in progress, the task is queued until one is available. Once
+a worker has completed a task, the task's callback is called.
+
+__Arguments__
+
+* worker(task, callback) - An asynchronous function for processing a queued
+  task, which must call its callback(err) argument when finished, with an 
+  optional error as an argument.
+* concurrency - An integer for determining how many worker functions should be
+  run in parallel.
+
+__Queue objects__
+
+The queue object returned by this function has the following properties and
+methods:
+
+* length() - a function returning the number of items waiting to be processed.
+* concurrency - an integer for determining how many worker functions should be
+  run in parallel. This property can be changed after a queue is created to
+  alter the concurrency on-the-fly.
+* push(task, [callback]) - add a new task to the queue, the callback is called
+  once the worker has finished processing the task.
+  instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
+* unshift(task, [callback]) - add a new task to the front of the queue.
+* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued
+* empty - a callback that is called when the last item from the queue is given to a worker
+* drain - a callback that is called when the last item from the queue has returned from the worker
+
+__Example__
+
+```js
+// create a queue object with concurrency 2
+
+var q = async.queue(function (task, callback) {
+    console.log('hello ' + task.name);
+    callback();
+}, 2);
+
+
+// assign a callback
+q.drain = function() {
+    console.log('all items have been processed');
+}
+
+// add some items to the queue
+
+q.push({name: 'foo'}, function (err) {
+    console.log('finished processing foo');
+});
+q.push({name: 'bar'}, function (err) {
+    console.log('finished processing bar');
+});
+
+// add some items to the queue (batch-wise)
+
+q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
+    console.log('finished processing bar');
+});
+
+// add some items to the front of the queue
+
+q.unshift({name: 'bar'}, function (err) {
+    console.log('finished processing bar');
+});
+```
+
+---------------------------------------
+
+<a name="cargo" />
+### cargo(worker, [payload])
+
+Creates a cargo object with the specified payload. Tasks added to the
+cargo will be processed altogether (up to the payload limit). If the
+worker is in progress, the task is queued until it is available. Once
+the worker has completed some tasks, each callback of those tasks is called.
+
+__Arguments__
+
+* worker(tasks, callback) - An asynchronous function for processing an array of
+  queued tasks, which must call its callback(err) argument when finished, with 
+  an optional error as an argument.
+* payload - An optional integer for determining how many tasks should be
+  processed per round; if omitted, the default is unlimited.
+
+__Cargo objects__
+
+The cargo object returned by this function has the following properties and
+methods:
+
+* length() - a function returning the number of items waiting to be processed.
+* payload - an integer for determining how many tasks should be
+  process per round. This property can be changed after a cargo is created to
+  alter the payload on-the-fly.
+* push(task, [callback]) - add a new task to the queue, the callback is called
+  once the worker has finished processing the task.
+  instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
+* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued
+* empty - a callback that is called when the last item from the queue is given to a worker
+* drain - a callback that is called when the last item from the queue has returned from the worker
+
+__Example__
+
+```js
+// create a cargo object with payload 2
+
+var cargo = async.cargo(function (tasks, callback) {
+    for(var i=0; i<tasks.length; i++){
+      console.log('hello ' + tasks[i].name);
+    }
+    callback();
+}, 2);
+
+
+// add some items
+
+cargo.push({name: 'foo'}, function (err) {
+    console.log('finished processing foo');
+});
+cargo.push({name: 'bar'}, function (err) {
+    console.log('finished processing bar');
+});
+cargo.push({name: 'baz'}, function (err) {
+    console.log('finished processing baz');
+});
+```
+
+---------------------------------------
+
+<a name="auto" />
+### auto(tasks, [callback])
+
+Determines the best order for running functions based on their requirements.
+Each function can optionally depend on other functions being completed first,
+and each function is run as soon as its requirements are satisfied. If any of
+the functions pass an error to their callback, that function will not complete
+(so any other functions depending on it will not run) and the main callback
+will be called immediately with the error. Functions also receive an object
+containing the results of functions which have completed so far.
+
+Note, all functions are called with a results object as a second argument, 
+so it is unsafe to pass functions in the tasks object which cannot handle the
+extra argument. For example, this snippet of code:
+
+```js
+async.auto({
+  readData: async.apply(fs.readFile, 'data.txt', 'utf-8')
+}, callback);
+```
+
+will have the effect of calling readFile with the results object as the last
+argument, which will fail:
+
+```js
+fs.readFile('data.txt', 'utf-8', cb, {});
+```
+
+Instead, wrap the call to readFile in a function which does not forward the 
+results object:
+
+```js
+async.auto({
+  readData: function(cb, results){
+    fs.readFile('data.txt', 'utf-8', cb);
+  }
+}, callback);
+```
+
+__Arguments__
+
+* tasks - An object literal containing named functions or an array of
+  requirements, with the function itself the last item in the array. The key
+  used for each function or array is used when specifying requirements. The 
+  function receives two arguments: (1) a callback(err, result) which must be 
+  called when finished, passing an error (which can be null) and the result of 
+  the function's execution, and (2) a results object, containing the results of
+  the previously executed functions.
+* callback(err, results) - An optional callback which is called when all the
+  tasks have been completed. The callback will receive an error as an argument
+  if any tasks pass an error to their callback. Results will always be passed
+	but if an error occurred, no other tasks will be performed, and the results
+	object will only contain partial results.
+  
+
+__Example__
+
+```js
+async.auto({
+    get_data: function(callback){
+        // async code to get some data
+    },
+    make_folder: function(callback){
+        // async code to create a directory to store a file in
+        // this is run at the same time as getting the data
+    },
+    write_file: ['get_data', 'make_folder', function(callback){
+        // once there is some data and the directory exists,
+        // write the data to a file in the directory
+        callback(null, filename);
+    }],
+    email_link: ['write_file', function(callback, results){
+        // once the file is written let's email a link to it...
+        // results.write_file contains the filename returned by write_file.
+    }]
+});
+```
+
+This is a fairly trivial example, but to do this using the basic parallel and
+series functions would look like this:
+
+```js
+async.parallel([
+    function(callback){
+        // async code to get some data
+    },
+    function(callback){
+        // async code to create a directory to store a file in
+        // this is run at the same time as getting the data
+    }
+],
+function(err, results){
+    async.series([
+        function(callback){
+            // once there is some data and the directory exists,
+            // write the data to a file in the directory
+        },
+        function(callback){
+            // once the file is written let's email a link to it...
+        }
+    ]);
+});
+```
+
+For a complicated series of async tasks using the auto function makes adding
+new tasks much easier and makes the code more readable.
+
+
+---------------------------------------
+
+<a name="iterator" />
+### iterator(tasks)
+
+Creates an iterator function which calls the next function in the array,
+returning a continuation to call the next one after that. It's also possible to
+'peek' the next iterator by doing iterator.next().
+
+This function is used internally by the async module but can be useful when
+you want to manually control the flow of functions in series.
+
+__Arguments__
+
+* tasks - An array of functions to run.
+
+__Example__
+
+```js
+var iterator = async.iterator([
+    function(){ sys.p('one'); },
+    function(){ sys.p('two'); },
+    function(){ sys.p('three'); }
+]);
+
+node> var iterator2 = iterator();
+'one'
+node> var iterator3 = iterator2();
+'two'
+node> iterator3();
+'three'
+node> var nextfn = iterator2.next();
+node> nextfn();
+'three'
+```
+
+---------------------------------------
+
+<a name="apply" />
+### apply(function, arguments..)
+
+Creates a continuation function with some arguments already applied, a useful
+shorthand when combined with other control flow functions. Any arguments
+passed to the returned function are added to the arguments originally passed
+to apply.
+
+__Arguments__
+
+* function - The function you want to eventually apply all arguments to.
+* arguments... - Any number of arguments to automatically apply when the
+  continuation is called.
+
+__Example__
+
+```js
+// using apply
+
+async.parallel([
+    async.apply(fs.writeFile, 'testfile1', 'test1'),
+    async.apply(fs.writeFile, 'testfile2', 'test2'),
+]);
+
+
+// the same process without using apply
+
+async.parallel([
+    function(callback){
+        fs.writeFile('testfile1', 'test1', callback);
+    },
+    function(callback){
+        fs.writeFile('testfile2', 'test2', callback);
+    }
+]);
+```
+
+It's possible to pass any number of additional arguments when calling the
+continuation:
+
+```js
+node> var fn = async.apply(sys.puts, 'one');
+node> fn('two', 'three');
+one
+two
+three
+```
+
+---------------------------------------
+
+<a name="nextTick" />
+### nextTick(callback)
+
+Calls the callback on a later loop around the event loop. In node.js this just
+calls process.nextTick, in the browser it falls back to setImmediate(callback)
+if available, otherwise setTimeout(callback, 0), which means other higher priority
+events may precede the execution of the callback.
+
+This is used internally for browser-compatibility purposes.
+
+__Arguments__
+
+* callback - The function to call on a later loop around the event loop.
+
+__Example__
+
+```js
+var call_order = [];
+async.nextTick(function(){
+    call_order.push('two');
+    // call_order now equals ['one','two']
+});
+call_order.push('one')
+```
+
+<a name="times" />
+### times(n, callback)
+
+Calls the callback n times and accumulates results in the same manner
+you would use with async.map.
+
+__Arguments__
+
+* n - The number of times to run the function.
+* callback - The function to call n times.
+
+__Example__
+
+```js
+// Pretend this is some complicated async factory
+var createUser = function(id, callback) {
+  callback(null, {
+    id: 'user' + id
+  })
+}
+// generate 5 users
+async.times(5, function(n, next){
+    createUser(n, function(err, user) {
+      next(err, user)
+    })
+}, function(err, users) {
+  // we should now have 5 users
+});
+```
+
+<a name="timesSeries" />
+### timesSeries(n, callback)
+
+The same as times only the iterator is applied to each item in the array in
+series. The next iterator is only called once the current one has completed
+processing. The results array will be in the same order as the original.
+
+
+## Utils
+
+<a name="memoize" />
+### memoize(fn, [hasher])
+
+Caches the results of an async function. When creating a hash to store function
+results against, the callback is omitted from the hash and an optional hash
+function can be used.
+
+The cache of results is exposed as the `memo` property of the function returned
+by `memoize`.
+
+__Arguments__
+
+* fn - the function you to proxy and cache results from.
+* hasher - an optional function for generating a custom hash for storing
+  results, it has all the arguments applied to it apart from the callback, and
+  must be synchronous.
+
+__Example__
+
+```js
+var slow_fn = function (name, callback) {
+    // do something
+    callback(null, result);
+};
+var fn = async.memoize(slow_fn);
+
+// fn can now be used as if it were slow_fn
+fn('some name', function () {
+    // callback
+});
+```
+
+<a name="unmemoize" />
+### unmemoize(fn)
+
+Undoes a memoized function, reverting it to the original, unmemoized
+form. Comes handy in tests.
+
+__Arguments__
+
+* fn - the memoized function
+
+<a name="log" />
+### log(function, arguments)
+
+Logs the result of an async function to the console. Only works in node.js or
+in browsers that support console.log and console.error (such as FF and Chrome).
+If multiple arguments are returned from the async function, console.log is
+called on each argument in order.
+
+__Arguments__
+
+* function - The function you want to eventually apply all arguments to.
+* arguments... - Any number of arguments to apply to the function.
+
+__Example__
+
+```js
+var hello = function(name, callback){
+    setTimeout(function(){
+        callback(null, 'hello ' + name);
+    }, 1000);
+};
+```
+```js
+node> async.log(hello, 'world');
+'hello world'
+```
+
+---------------------------------------
+
+<a name="dir" />
+### dir(function, arguments)
+
+Logs the result of an async function to the console using console.dir to
+display the properties of the resulting object. Only works in node.js or
+in browsers that support console.dir and console.error (such as FF and Chrome).
+If multiple arguments are returned from the async function, console.dir is
+called on each argument in order.
+
+__Arguments__
+
+* function - The function you want to eventually apply all arguments to.
+* arguments... - Any number of arguments to apply to the function.
+
+__Example__
+
+```js
+var hello = function(name, callback){
+    setTimeout(function(){
+        callback(null, {hello: name});
+    }, 1000);
+};
+```
+```js
+node> async.dir(hello, 'world');
+{hello: 'world'}
+```
+
+---------------------------------------
+
+<a name="noConflict" />
+### noConflict()
+
+Changes the value of async back to its original value, returning a reference to the
+async object.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/component.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/component.json
new file mode 100644
index 0000000..bbb0115
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/component.json
@@ -0,0 +1,11 @@
+{
+  "name": "async",
+  "repo": "caolan/async",
+  "description": "Higher-order functions and common patterns for asynchronous code",
+  "version": "0.1.23",
+  "keywords": [],
+  "dependencies": {},
+  "development": {},
+  "main": "lib/async.js",
+  "scripts": [ "lib/async.js" ]
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/lib/async.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/lib/async.js
new file mode 100644
index 0000000..1eebb15
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/lib/async.js
@@ -0,0 +1,958 @@
+/*global setImmediate: false, setTimeout: false, console: false */
+(function () {
+
+    var async = {};
+
+    // global on the server, window in the browser
+    var root, previous_async;
+
+    root = this;
+    if (root != null) {
+      previous_async = root.async;
+    }
+
+    async.noConflict = function () {
+        root.async = previous_async;
+        return async;
+    };
+
+    function only_once(fn) {
+        var called = false;
+        return function() {
+            if (called) throw new Error("Callback was already called.");
+            called = true;
+            fn.apply(root, arguments);
+        }
+    }
+
+    //// cross-browser compatiblity functions ////
+
+    var _each = function (arr, iterator) {
+        if (arr.forEach) {
+            return arr.forEach(iterator);
+        }
+        for (var i = 0; i < arr.length; i += 1) {
+            iterator(arr[i], i, arr);
+        }
+    };
+
+    var _map = function (arr, iterator) {
+        if (arr.map) {
+            return arr.map(iterator);
+        }
+        var results = [];
+        _each(arr, function (x, i, a) {
+            results.push(iterator(x, i, a));
+        });
+        return results;
+    };
+
+    var _reduce = function (arr, iterator, memo) {
+        if (arr.reduce) {
+            return arr.reduce(iterator, memo);
+        }
+        _each(arr, function (x, i, a) {
+            memo = iterator(memo, x, i, a);
+        });
+        return memo;
+    };
+
+    var _keys = function (obj) {
+        if (Object.keys) {
+            return Object.keys(obj);
+        }
+        var keys = [];
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                keys.push(k);
+            }
+        }
+        return keys;
+    };
+
+    //// exported async module functions ////
+
+    //// nextTick implementation with browser-compatible fallback ////
+    if (typeof process === 'undefined' || !(process.nextTick)) {
+        if (typeof setImmediate === 'function') {
+            async.nextTick = function (fn) {
+                // not a direct alias for IE10 compatibility
+                setImmediate(fn);
+            };
+            async.setImmediate = async.nextTick;
+        }
+        else {
+            async.nextTick = function (fn) {
+                setTimeout(fn, 0);
+            };
+            async.setImmediate = async.nextTick;
+        }
+    }
+    else {
+        async.nextTick = process.nextTick;
+        if (typeof setImmediate !== 'undefined') {
+            async.setImmediate = function (fn) {
+              // not a direct alias for IE10 compatibility
+              setImmediate(fn);
+            };
+        }
+        else {
+            async.setImmediate = async.nextTick;
+        }
+    }
+
+    async.each = function (arr, iterator, callback) {
+        callback = callback || function () {};
+        if (!arr.length) {
+            return callback();
+        }
+        var completed = 0;
+        _each(arr, function (x) {
+            iterator(x, only_once(function (err) {
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                }
+                else {
+                    completed += 1;
+                    if (completed >= arr.length) {
+                        callback(null);
+                    }
+                }
+            }));
+        });
+    };
+    async.forEach = async.each;
+
+    async.eachSeries = function (arr, iterator, callback) {
+        callback = callback || function () {};
+        if (!arr.length) {
+            return callback();
+        }
+        var completed = 0;
+        var iterate = function () {
+            iterator(arr[completed], function (err) {
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                }
+                else {
+                    completed += 1;
+                    if (completed >= arr.length) {
+                        callback(null);
+                    }
+                    else {
+                        iterate();
+                    }
+                }
+            });
+        };
+        iterate();
+    };
+    async.forEachSeries = async.eachSeries;
+
+    async.eachLimit = function (arr, limit, iterator, callback) {
+        var fn = _eachLimit(limit);
+        fn.apply(null, [arr, iterator, callback]);
+    };
+    async.forEachLimit = async.eachLimit;
+
+    var _eachLimit = function (limit) {
+
+        return function (arr, iterator, callback) {
+            callback = callback || function () {};
+            if (!arr.length || limit <= 0) {
+                return callback();
+            }
+            var completed = 0;
+            var started = 0;
+            var running = 0;
+
+            (function replenish () {
+                if (completed >= arr.length) {
+                    return callback();
+                }
+
+                while (running < limit && started < arr.length) {
+                    started += 1;
+                    running += 1;
+                    iterator(arr[started - 1], function (err) {
+                        if (err) {
+                            callback(err);
+                            callback = function () {};
+                        }
+                        else {
+                            completed += 1;
+                            running -= 1;
+                            if (completed >= arr.length) {
+                                callback();
+                            }
+                            else {
+                                replenish();
+                            }
+                        }
+                    });
+                }
+            })();
+        };
+    };
+
+
+    var doParallel = function (fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [async.each].concat(args));
+        };
+    };
+    var doParallelLimit = function(limit, fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [_eachLimit(limit)].concat(args));
+        };
+    };
+    var doSeries = function (fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [async.eachSeries].concat(args));
+        };
+    };
+
+
+    var _asyncMap = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (err, v) {
+                results[x.index] = v;
+                callback(err);
+            });
+        }, function (err) {
+            callback(err, results);
+        });
+    };
+    async.map = doParallel(_asyncMap);
+    async.mapSeries = doSeries(_asyncMap);
+    async.mapLimit = function (arr, limit, iterator, callback) {
+        return _mapLimit(limit)(arr, iterator, callback);
+    };
+
+    var _mapLimit = function(limit) {
+        return doParallelLimit(limit, _asyncMap);
+    };
+
+    // reduce only has a series version, as doing reduce in parallel won't
+    // work in many situations.
+    async.reduce = function (arr, memo, iterator, callback) {
+        async.eachSeries(arr, function (x, callback) {
+            iterator(memo, x, function (err, v) {
+                memo = v;
+                callback(err);
+            });
+        }, function (err) {
+            callback(err, memo);
+        });
+    };
+    // inject alias
+    async.inject = async.reduce;
+    // foldl alias
+    async.foldl = async.reduce;
+
+    async.reduceRight = function (arr, memo, iterator, callback) {
+        var reversed = _map(arr, function (x) {
+            return x;
+        }).reverse();
+        async.reduce(reversed, memo, iterator, callback);
+    };
+    // foldr alias
+    async.foldr = async.reduceRight;
+
+    var _filter = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (v) {
+                if (v) {
+                    results.push(x);
+                }
+                callback();
+            });
+        }, function (err) {
+            callback(_map(results.sort(function (a, b) {
+                return a.index - b.index;
+            }), function (x) {
+                return x.value;
+            }));
+        });
+    };
+    async.filter = doParallel(_filter);
+    async.filterSeries = doSeries(_filter);
+    // select alias
+    async.select = async.filter;
+    async.selectSeries = async.filterSeries;
+
+    var _reject = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (v) {
+                if (!v) {
+                    results.push(x);
+                }
+                callback();
+            });
+        }, function (err) {
+            callback(_map(results.sort(function (a, b) {
+                return a.index - b.index;
+            }), function (x) {
+                return x.value;
+            }));
+        });
+    };
+    async.reject = doParallel(_reject);
+    async.rejectSeries = doSeries(_reject);
+
+    var _detect = function (eachfn, arr, iterator, main_callback) {
+        eachfn(arr, function (x, callback) {
+            iterator(x, function (result) {
+                if (result) {
+                    main_callback(x);
+                    main_callback = function () {};
+                }
+                else {
+                    callback();
+                }
+            });
+        }, function (err) {
+            main_callback();
+        });
+    };
+    async.detect = doParallel(_detect);
+    async.detectSeries = doSeries(_detect);
+
+    async.some = function (arr, iterator, main_callback) {
+        async.each(arr, function (x, callback) {
+            iterator(x, function (v) {
+                if (v) {
+                    main_callback(true);
+                    main_callback = function () {};
+                }
+                callback();
+            });
+        }, function (err) {
+            main_callback(false);
+        });
+    };
+    // any alias
+    async.any = async.some;
+
+    async.every = function (arr, iterator, main_callback) {
+        async.each(arr, function (x, callback) {
+            iterator(x, function (v) {
+                if (!v) {
+                    main_callback(false);
+                    main_callback = function () {};
+                }
+                callback();
+            });
+        }, function (err) {
+            main_callback(true);
+        });
+    };
+    // all alias
+    async.all = async.every;
+
+    async.sortBy = function (arr, iterator, callback) {
+        async.map(arr, function (x, callback) {
+            iterator(x, function (err, criteria) {
+                if (err) {
+                    callback(err);
+                }
+                else {
+                    callback(null, {value: x, criteria: criteria});
+                }
+            });
+        }, function (err, results) {
+            if (err) {
+                return callback(err);
+            }
+            else {
+                var fn = function (left, right) {
+                    var a = left.criteria, b = right.criteria;
+                    return a < b ? -1 : a > b ? 1 : 0;
+                };
+                callback(null, _map(results.sort(fn), function (x) {
+                    return x.value;
+                }));
+            }
+        });
+    };
+
+    async.auto = function (tasks, callback) {
+        callback = callback || function () {};
+        var keys = _keys(tasks);
+        if (!keys.length) {
+            return callback(null);
+        }
+
+        var results = {};
+
+        var listeners = [];
+        var addListener = function (fn) {
+            listeners.unshift(fn);
+        };
+        var removeListener = function (fn) {
+            for (var i = 0; i < listeners.length; i += 1) {
+                if (listeners[i] === fn) {
+                    listeners.splice(i, 1);
+                    return;
+                }
+            }
+        };
+        var taskComplete = function () {
+            _each(listeners.slice(0), function (fn) {
+                fn();
+            });
+        };
+
+        addListener(function () {
+            if (_keys(results).length === keys.length) {
+                callback(null, results);
+                callback = function () {};
+            }
+        });
+
+        _each(keys, function (k) {
+            var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
+            var taskCallback = function (err) {
+                var args = Array.prototype.slice.call(arguments, 1);
+                if (args.length <= 1) {
+                    args = args[0];
+                }
+                if (err) {
+                    var safeResults = {};
+                    _each(_keys(results), function(rkey) {
+                        safeResults[rkey] = results[rkey];
+                    });
+                    safeResults[k] = args;
+                    callback(err, safeResults);
+                    // stop subsequent errors hitting callback multiple times
+                    callback = function () {};
+                }
+                else {
+                    results[k] = args;
+                    async.setImmediate(taskComplete);
+                }
+            };
+            var requires = task.slice(0, Math.abs(task.length - 1)) || [];
+            var ready = function () {
+                return _reduce(requires, function (a, x) {
+                    return (a && results.hasOwnProperty(x));
+                }, true) && !results.hasOwnProperty(k);
+            };
+            if (ready()) {
+                task[task.length - 1](taskCallback, results);
+            }
+            else {
+                var listener = function () {
+                    if (ready()) {
+                        removeListener(listener);
+                        task[task.length - 1](taskCallback, results);
+                    }
+                };
+                addListener(listener);
+            }
+        });
+    };
+
+    async.waterfall = function (tasks, callback) {
+        callback = callback || function () {};
+        if (tasks.constructor !== Array) {
+          var err = new Error('First argument to waterfall must be an array of functions');
+          return callback(err);
+        }
+        if (!tasks.length) {
+            return callback();
+        }
+        var wrapIterator = function (iterator) {
+            return function (err) {
+                if (err) {
+                    callback.apply(null, arguments);
+                    callback = function () {};
+                }
+                else {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    var next = iterator.next();
+                    if (next) {
+                        args.push(wrapIterator(next));
+                    }
+                    else {
+                        args.push(callback);
+                    }
+                    async.setImmediate(function () {
+                        iterator.apply(null, args);
+                    });
+                }
+            };
+        };
+        wrapIterator(async.iterator(tasks))();
+    };
+
+    var _parallel = function(eachfn, tasks, callback) {
+        callback = callback || function () {};
+        if (tasks.constructor === Array) {
+            eachfn.map(tasks, function (fn, callback) {
+                if (fn) {
+                    fn(function (err) {
+                        var args = Array.prototype.slice.call(arguments, 1);
+                        if (args.length <= 1) {
+                            args = args[0];
+                        }
+                        callback.call(null, err, args);
+                    });
+                }
+            }, callback);
+        }
+        else {
+            var results = {};
+            eachfn.each(_keys(tasks), function (k, callback) {
+                tasks[k](function (err) {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    if (args.length <= 1) {
+                        args = args[0];
+                    }
+                    results[k] = args;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+
+    async.parallel = function (tasks, callback) {
+        _parallel({ map: async.map, each: async.each }, tasks, callback);
+    };
+
+    async.parallelLimit = function(tasks, limit, callback) {
+        _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
+    };
+
+    async.series = function (tasks, callback) {
+        callback = callback || function () {};
+        if (tasks.constructor === Array) {
+            async.mapSeries(tasks, function (fn, callback) {
+                if (fn) {
+                    fn(function (err) {
+                        var args = Array.prototype.slice.call(arguments, 1);
+                        if (args.length <= 1) {
+                            args = args[0];
+                        }
+                        callback.call(null, err, args);
+                    });
+                }
+            }, callback);
+        }
+        else {
+            var results = {};
+            async.eachSeries(_keys(tasks), function (k, callback) {
+                tasks[k](function (err) {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    if (args.length <= 1) {
+                        args = args[0];
+                    }
+                    results[k] = args;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+
+    async.iterator = function (tasks) {
+        var makeCallback = function (index) {
+            var fn = function () {
+                if (tasks.length) {
+                    tasks[index].apply(null, arguments);
+                }
+                return fn.next();
+            };
+            fn.next = function () {
+                return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+            };
+            return fn;
+        };
+        return makeCallback(0);
+    };
+
+    async.apply = function (fn) {
+        var args = Array.prototype.slice.call(arguments, 1);
+        return function () {
+            return fn.apply(
+                null, args.concat(Array.prototype.slice.call(arguments))
+            );
+        };
+    };
+
+    var _concat = function (eachfn, arr, fn, callback) {
+        var r = [];
+        eachfn(arr, function (x, cb) {
+            fn(x, function (err, y) {
+                r = r.concat(y || []);
+                cb(err);
+            });
+        }, function (err) {
+            callback(err, r);
+        });
+    };
+    async.concat = doParallel(_concat);
+    async.concatSeries = doSeries(_concat);
+
+    async.whilst = function (test, iterator, callback) {
+        if (test()) {
+            iterator(function (err) {
+                if (err) {
+                    return callback(err);
+                }
+                async.whilst(test, iterator, callback);
+            });
+        }
+        else {
+            callback();
+        }
+    };
+
+    async.doWhilst = function (iterator, test, callback) {
+        iterator(function (err) {
+            if (err) {
+                return callback(err);
+            }
+            if (test()) {
+                async.doWhilst(iterator, test, callback);
+            }
+            else {
+                callback();
+            }
+        });
+    };
+
+    async.until = function (test, iterator, callback) {
+        if (!test()) {
+            iterator(function (err) {
+                if (err) {
+                    return callback(err);
+                }
+                async.until(test, iterator, callback);
+            });
+        }
+        else {
+            callback();
+        }
+    };
+
+    async.doUntil = function (iterator, test, callback) {
+        iterator(function (err) {
+            if (err) {
+                return callback(err);
+            }
+            if (!test()) {
+                async.doUntil(iterator, test, callback);
+            }
+            else {
+                callback();
+            }
+        });
+    };
+
+    async.queue = function (worker, concurrency) {
+        if (concurrency === undefined) {
+            concurrency = 1;
+        }
+        function _insert(q, data, pos, callback) {
+          if(data.constructor !== Array) {
+              data = [data];
+          }
+          _each(data, function(task) {
+              var item = {
+                  data: task,
+                  callback: typeof callback === 'function' ? callback : null
+              };
+
+              if (pos) {
+                q.tasks.unshift(item);
+              } else {
+                q.tasks.push(item);
+              }
+
+              if (q.saturated && q.tasks.length === concurrency) {
+                  q.saturated();
+              }
+              async.setImmediate(q.process);
+          });
+        }
+
+        var workers = 0;
+        var q = {
+            tasks: [],
+            concurrency: concurrency,
+            saturated: null,
+            empty: null,
+            drain: null,
+            push: function (data, callback) {
+              _insert(q, data, false, callback);
+            },
+            unshift: function (data, callback) {
+              _insert(q, data, true, callback);
+            },
+            process: function () {
+                if (workers < q.concurrency && q.tasks.length) {
+                    var task = q.tasks.shift();
+                    if (q.empty && q.tasks.length === 0) {
+                        q.empty();
+                    }
+                    workers += 1;
+                    var next = function () {
+                        workers -= 1;
+                        if (task.callback) {
+                            task.callback.apply(task, arguments);
+                        }
+                        if (q.drain && q.tasks.length + workers === 0) {
+                            q.drain();
+                        }
+                        q.process();
+                    };
+                    var cb = only_once(next);
+                    worker(task.data, cb);
+                }
+            },
+            length: function () {
+                return q.tasks.length;
+            },
+            running: function () {
+                return workers;
+            }
+        };
+        return q;
+    };
+
+    async.cargo = function (worker, payload) {
+        var working     = false,
+            tasks       = [];
+
+        var cargo = {
+            tasks: tasks,
+            payload: payload,
+            saturated: null,
+            empty: null,
+            drain: null,
+            push: function (data, callback) {
+                if(data.constructor !== Array) {
+                    data = [data];
+                }
+                _each(data, function(task) {
+                    tasks.push({
+                        data: task,
+                        callback: typeof callback === 'function' ? callback : null
+                    });
+                    if (cargo.saturated && tasks.length === payload) {
+                        cargo.saturated();
+                    }
+                });
+                async.setImmediate(cargo.process);
+            },
+            process: function process() {
+                if (working) return;
+                if (tasks.length === 0) {
+                    if(cargo.drain) cargo.drain();
+                    return;
+                }
+
+                var ts = typeof payload === 'number'
+                            ? tasks.splice(0, payload)
+                            : tasks.splice(0);
+
+                var ds = _map(ts, function (task) {
+                    return task.data;
+                });
+
+                if(cargo.empty) cargo.empty();
+                working = true;
+                worker(ds, function () {
+                    working = false;
+
+                    var args = arguments;
+                    _each(ts, function (data) {
+                        if (data.callback) {
+                            data.callback.apply(null, args);
+                        }
+                    });
+
+                    process();
+                });
+            },
+            length: function () {
+                return tasks.length;
+            },
+            running: function () {
+                return working;
+            }
+        };
+        return cargo;
+    };
+
+    var _console_fn = function (name) {
+        return function (fn) {
+            var args = Array.prototype.slice.call(arguments, 1);
+            fn.apply(null, args.concat([function (err) {
+                var args = Array.prototype.slice.call(arguments, 1);
+                if (typeof console !== 'undefined') {
+                    if (err) {
+                        if (console.error) {
+                            console.error(err);
+                        }
+                    }
+                    else if (console[name]) {
+                        _each(args, function (x) {
+                            console[name](x);
+                        });
+                    }
+                }
+            }]));
+        };
+    };
+    async.log = _console_fn('log');
+    async.dir = _console_fn('dir');
+    /*async.info = _console_fn('info');
+    async.warn = _console_fn('warn');
+    async.error = _console_fn('error');*/
+
+    async.memoize = function (fn, hasher) {
+        var memo = {};
+        var queues = {};
+        hasher = hasher || function (x) {
+            return x;
+        };
+        var memoized = function () {
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            var key = hasher.apply(null, args);
+            if (key in memo) {
+                callback.apply(null, memo[key]);
+            }
+            else if (key in queues) {
+                queues[key].push(callback);
+            }
+            else {
+                queues[key] = [callback];
+                fn.apply(null, args.concat([function () {
+                    memo[key] = arguments;
+                    var q = queues[key];
+                    delete queues[key];
+                    for (var i = 0, l = q.length; i < l; i++) {
+                      q[i].apply(null, arguments);
+                    }
+                }]));
+            }
+        };
+        memoized.memo = memo;
+        memoized.unmemoized = fn;
+        return memoized;
+    };
+
+    async.unmemoize = function (fn) {
+      return function () {
+        return (fn.unmemoized || fn).apply(null, arguments);
+      };
+    };
+
+    async.times = function (count, iterator, callback) {
+        var counter = [];
+        for (var i = 0; i < count; i++) {
+            counter.push(i);
+        }
+        return async.map(counter, iterator, callback);
+    };
+
+    async.timesSeries = function (count, iterator, callback) {
+        var counter = [];
+        for (var i = 0; i < count; i++) {
+            counter.push(i);
+        }
+        return async.mapSeries(counter, iterator, callback);
+    };
+
+    async.compose = function (/* functions... */) {
+        var fns = Array.prototype.reverse.call(arguments);
+        return function () {
+            var that = this;
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            async.reduce(fns, args, function (newargs, fn, cb) {
+                fn.apply(that, newargs.concat([function () {
+                    var err = arguments[0];
+                    var nextargs = Array.prototype.slice.call(arguments, 1);
+                    cb(err, nextargs);
+                }]))
+            },
+            function (err, results) {
+                callback.apply(that, [err].concat(results));
+            });
+        };
+    };
+
+    var _applyEach = function (eachfn, fns /*args...*/) {
+        var go = function () {
+            var that = this;
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            return eachfn(fns, function (fn, cb) {
+                fn.apply(that, args.concat([cb]));
+            },
+            callback);
+        };
+        if (arguments.length > 2) {
+            var args = Array.prototype.slice.call(arguments, 2);
+            return go.apply(this, args);
+        }
+        else {
+            return go;
+        }
+    };
+    async.applyEach = doParallel(_applyEach);
+    async.applyEachSeries = doSeries(_applyEach);
+
+    async.forever = function (fn, callback) {
+        function next(err) {
+            if (err) {
+                if (callback) {
+                    return callback(err);
+                }
+                throw err;
+            }
+            fn(next);
+        }
+        next();
+    };
+
+    // AMD / RequireJS
+    if (typeof define !== 'undefined' && define.amd) {
+        define([], function () {
+            return async;
+        });
+    }
+    // Node.js
+    else if (typeof module !== 'undefined' && module.exports) {
+        module.exports = async;
+    }
+    // included directly via <script> tag
+    else {
+        root.async = async;
+    }
+
+}());
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/package.json
new file mode 100644
index 0000000..e2d292d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/async/package.json
@@ -0,0 +1,60 @@
+{
+  "name": "async",
+  "description": "Higher-order functions and common patterns for asynchronous code",
+  "main": "./lib/async",
+  "author": {
+    "name": "Caolan McMahon"
+  },
+  "version": "0.2.10",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/caolan/async.git"
+  },
+  "bugs": {
+    "url": "https://github.com/caolan/async/issues"
+  },
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/caolan/async/raw/master/LICENSE"
+    }
+  ],
+  "devDependencies": {
+    "nodeunit": ">0.0.0",
+    "uglify-js": "1.2.x",
+    "nodelint": ">0.0.0"
+  },
+  "jam": {
+    "main": "lib/async.js",
+    "include": [
+      "lib/async.js",
+      "README.md",
+      "LICENSE"
+    ]
+  },
+  "scripts": {
+    "test": "nodeunit test/test-async.js"
+  },
+  "_id": "async@0.2.10",
+  "dist": {
+    "shasum": "b6bbe0b0674b9d719708ca38de8c237cb526c3d1",
+    "tarball": "http://registry.npmjs.org/async/-/async-0.2.10.tgz"
+  },
+  "_from": "async@>=0.2.6 <0.3.0",
+  "_npmVersion": "1.3.2",
+  "_npmUser": {
+    "name": "caolan",
+    "email": "caolan.mcmahon@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "caolan",
+      "email": "caolan@caolanmcmahon.com"
+    }
+  ],
+  "directories": {},
+  "_shasum": "b6bbe0b0674b9d719708ca38de8c237cb526c3d1",
+  "_resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+  "readme": "ERROR: No README data found!",
+  "homepage": "https://github.com/caolan/async"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.npmignore
new file mode 100644
index 0000000..3dddf3f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.npmignore
@@ -0,0 +1,2 @@
+dist/*
+node_modules/*
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.tern-port b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.tern-port
new file mode 100644
index 0000000..79d76a0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.tern-port
@@ -0,0 +1 @@
+55494
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.travis.yml
new file mode 100644
index 0000000..ddc9c4f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+  - 0.8
+  - "0.10"
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/CHANGELOG.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/CHANGELOG.md
new file mode 100644
index 0000000..240d54a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/CHANGELOG.md
@@ -0,0 +1,137 @@
+# Change Log
+
+## 0.1.34
+
+* Make `SourceNode` work with windows style ("\r\n") newlines. Issue #103.
+
+* Fix bug involving source contents and the
+  `SourceMapGenerator.prototype.applySourceMap`. Issue #100.
+
+## 0.1.33
+
+* Fix some edge cases surrounding path joining and URL resolution.
+
+* Add a third parameter for relative path to
+  `SourceMapGenerator.prototype.applySourceMap`.
+
+* Fix issues with mappings and EOLs.
+
+## 0.1.32
+
+* Fixed a bug where SourceMapConsumer couldn't handle negative relative columns
+  (issue 92).
+
+* Fixed test runner to actually report number of failed tests as its process
+  exit code.
+
+* Fixed a typo when reporting bad mappings (issue 87).
+
+## 0.1.31
+
+* Delay parsing the mappings in SourceMapConsumer until queried for a source
+  location.
+
+* Support Sass source maps (which at the time of writing deviate from the spec
+  in small ways) in SourceMapConsumer.
+
+## 0.1.30
+
+* Do not join source root with a source, when the source is a data URI.
+
+* Extend the test runner to allow running single specific test files at a time.
+
+* Performance improvements in `SourceNode.prototype.walk` and
+  `SourceMapConsumer.prototype.eachMapping`.
+
+* Source map browser builds will now work inside Workers.
+
+* Better error messages when attempting to add an invalid mapping to a
+  `SourceMapGenerator`.
+
+## 0.1.29
+
+* Allow duplicate entries in the `names` and `sources` arrays of source maps
+  (usually from TypeScript) we are parsing. Fixes github issue 72.
+
+## 0.1.28
+
+* Skip duplicate mappings when creating source maps from SourceNode; github
+  issue 75.
+
+## 0.1.27
+
+* Don't throw an error when the `file` property is missing in SourceMapConsumer,
+  we don't use it anyway.
+
+## 0.1.26
+
+* Fix SourceNode.fromStringWithSourceMap for empty maps. Fixes github issue 70.
+
+## 0.1.25
+
+* Make compatible with browserify
+
+## 0.1.24
+
+* Fix issue with absolute paths and `file://` URIs. See
+  https://bugzilla.mozilla.org/show_bug.cgi?id=885597
+
+## 0.1.23
+
+* Fix issue with absolute paths and sourcesContent, github issue 64.
+
+## 0.1.22
+
+* Ignore duplicate mappings in SourceMapGenerator. Fixes github issue 21.
+
+## 0.1.21
+
+* Fixed handling of sources that start with a slash so that they are relative to
+  the source root's host.
+
+## 0.1.20
+
+* Fixed github issue #43: absolute URLs aren't joined with the source root
+  anymore.
+
+## 0.1.19
+
+* Using Travis CI to run tests.
+
+## 0.1.18
+
+* Fixed a bug in the handling of sourceRoot.
+
+## 0.1.17
+
+* Added SourceNode.fromStringWithSourceMap.
+
+## 0.1.16
+
+* Added missing documentation.
+
+* Fixed the generating of empty mappings in SourceNode.
+
+## 0.1.15
+
+* Added SourceMapGenerator.applySourceMap.
+
+## 0.1.14
+
+* The sourceRoot is now handled consistently.
+
+## 0.1.13
+
+* Added SourceMapGenerator.fromSourceMap.
+
+## 0.1.12
+
+* SourceNode now generates empty mappings too.
+
+## 0.1.11
+
+* Added name support to SourceNode.
+
+## 0.1.10
+
+* Added sourcesContent support to the customer and generator.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/LICENSE
new file mode 100644
index 0000000..ed1b7cf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/LICENSE
@@ -0,0 +1,28 @@
+
+Copyright (c) 2009-2011, Mozilla Foundation and contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the names of the Mozilla Foundation nor the names of project
+  contributors may be used to endorse or promote products derived from this
+  software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/Makefile.dryice.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/Makefile.dryice.js
new file mode 100644
index 0000000..d6fc26a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/Makefile.dryice.js
@@ -0,0 +1,166 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+var path = require('path');
+var fs = require('fs');
+var copy = require('dryice').copy;
+
+function removeAmdefine(src) {
+  src = String(src).replace(
+    /if\s*\(typeof\s*define\s*!==\s*'function'\)\s*{\s*var\s*define\s*=\s*require\('amdefine'\)\(module,\s*require\);\s*}\s*/g,
+    '');
+  src = src.replace(
+    /\b(define\(.*)('amdefine',?)/gm,
+    '$1');
+  return src;
+}
+removeAmdefine.onRead = true;
+
+function makeNonRelative(src) {
+  return src
+    .replace(/require\('.\//g, 'require(\'source-map/')
+    .replace(/\.\.\/\.\.\/lib\//g, '');
+}
+makeNonRelative.onRead = true;
+
+function buildBrowser() {
+  console.log('\nCreating dist/source-map.js');
+
+  var project = copy.createCommonJsProject({
+    roots: [ path.join(__dirname, 'lib') ]
+  });
+
+  copy({
+    source: [
+      'build/mini-require.js',
+      {
+        project: project,
+        require: [ 'source-map/source-map-generator',
+                   'source-map/source-map-consumer',
+                   'source-map/source-node']
+      },
+      'build/suffix-browser.js'
+    ],
+    filter: [
+      copy.filter.moduleDefines,
+      removeAmdefine
+    ],
+    dest: 'dist/source-map.js'
+  });
+}
+
+function buildBrowserMin() {
+  console.log('\nCreating dist/source-map.min.js');
+
+  copy({
+    source: 'dist/source-map.js',
+    filter: copy.filter.uglifyjs,
+    dest: 'dist/source-map.min.js'
+  });
+}
+
+function buildFirefox() {
+  console.log('\nCreating dist/SourceMap.jsm');
+
+  var project = copy.createCommonJsProject({
+    roots: [ path.join(__dirname, 'lib') ]
+  });
+
+  copy({
+    source: [
+      'build/prefix-source-map.jsm',
+      {
+        project: project,
+        require: [ 'source-map/source-map-consumer',
+                   'source-map/source-map-generator',
+                   'source-map/source-node' ]
+      },
+      'build/suffix-source-map.jsm'
+    ],
+    filter: [
+      copy.filter.moduleDefines,
+      removeAmdefine,
+      makeNonRelative
+    ],
+    dest: 'dist/SourceMap.jsm'
+  });
+
+  // Create dist/test/Utils.jsm
+  console.log('\nCreating dist/test/Utils.jsm');
+
+  project = copy.createCommonJsProject({
+    roots: [ __dirname, path.join(__dirname, 'lib') ]
+  });
+
+  copy({
+    source: [
+      'build/prefix-utils.jsm',
+      'build/assert-shim.js',
+      {
+        project: project,
+        require: [ 'test/source-map/util' ]
+      },
+      'build/suffix-utils.jsm'
+    ],
+    filter: [
+      copy.filter.moduleDefines,
+      removeAmdefine,
+      makeNonRelative
+    ],
+    dest: 'dist/test/Utils.jsm'
+  });
+
+  function isTestFile(f) {
+    return /^test\-.*?\.js/.test(f);
+  }
+
+  var testFiles = fs.readdirSync(path.join(__dirname, 'test', 'source-map')).filter(isTestFile);
+
+  testFiles.forEach(function (testFile) {
+    console.log('\nCreating', path.join('dist', 'test', testFile.replace(/\-/g, '_')));
+
+    copy({
+      source: [
+        'build/test-prefix.js',
+        path.join('test', 'source-map', testFile),
+        'build/test-suffix.js'
+      ],
+      filter: [
+        removeAmdefine,
+        makeNonRelative,
+        function (input, source) {
+          return input.replace('define(',
+                               'define("'
+                               + path.join('test', 'source-map', testFile.replace(/\.js$/, ''))
+                               + '", ["require", "exports", "module"], ');
+        },
+        function (input, source) {
+          return input.replace('{THIS_MODULE}', function () {
+            return "test/source-map/" + testFile.replace(/\.js$/, '');
+          });
+        }
+      ],
+      dest: path.join('dist', 'test', testFile.replace(/\-/g, '_'))
+    });
+  });
+}
+
+function ensureDir(name) {
+  var dirExists = false;
+  try {
+    dirExists = fs.statSync(name).isDirectory();
+  } catch (err) {}
+
+  if (!dirExists) {
+    fs.mkdirSync(name, 0777);
+  }
+}
+
+ensureDir("dist");
+ensureDir("dist/test");
+buildFirefox();
+buildBrowser();
+buildBrowserMin();
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/README.md
new file mode 100644
index 0000000..b00e970
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/README.md
@@ -0,0 +1,446 @@
+# Source Map
+
+This is a library to generate and consume the source map format
+[described here][format].
+
+This library is written in the Asynchronous Module Definition format, and works
+in the following environments:
+
+* Modern Browsers supporting ECMAScript 5 (either after the build, or with an
+  AMD loader such as RequireJS)
+
+* Inside Firefox (as a JSM file, after the build)
+
+* With NodeJS versions 0.8.X and higher
+
+## Node
+
+    $ npm install source-map
+
+## Building from Source (for everywhere else)
+
+Install Node and then run
+
+    $ git clone https://fitzgen@github.com/mozilla/source-map.git
+    $ cd source-map
+    $ npm link .
+
+Next, run
+
+    $ node Makefile.dryice.js
+
+This should spew a bunch of stuff to stdout, and create the following files:
+
+* `dist/source-map.js` - The unminified browser version.
+
+* `dist/source-map.min.js` - The minified browser version.
+
+* `dist/SourceMap.jsm` - The JavaScript Module for inclusion in Firefox source.
+
+## Examples
+
+### Consuming a source map
+
+    var rawSourceMap = {
+      version: 3,
+      file: 'min.js',
+      names: ['bar', 'baz', 'n'],
+      sources: ['one.js', 'two.js'],
+      sourceRoot: 'http://example.com/www/js/',
+      mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+    };
+
+    var smc = new SourceMapConsumer(rawSourceMap);
+
+    console.log(smc.sources);
+    // [ 'http://example.com/www/js/one.js',
+    //   'http://example.com/www/js/two.js' ]
+
+    console.log(smc.originalPositionFor({
+      line: 2,
+      column: 28
+    }));
+    // { source: 'http://example.com/www/js/two.js',
+    //   line: 2,
+    //   column: 10,
+    //   name: 'n' }
+
+    console.log(smc.generatedPositionFor({
+      source: 'http://example.com/www/js/two.js',
+      line: 2,
+      column: 10
+    }));
+    // { line: 2, column: 28 }
+
+    smc.eachMapping(function (m) {
+      // ...
+    });
+
+### Generating a source map
+
+In depth guide:
+[**Compiling to JavaScript, and Debugging with Source Maps**](https://hacks.mozilla.org/2013/05/compiling-to-javascript-and-debugging-with-source-maps/)
+
+#### With SourceNode (high level API)
+
+    function compile(ast) {
+      switch (ast.type) {
+      case 'BinaryExpression':
+        return new SourceNode(
+          ast.location.line,
+          ast.location.column,
+          ast.location.source,
+          [compile(ast.left), " + ", compile(ast.right)]
+        );
+      case 'Literal':
+        return new SourceNode(
+          ast.location.line,
+          ast.location.column,
+          ast.location.source,
+          String(ast.value)
+        );
+      // ...
+      default:
+        throw new Error("Bad AST");
+      }
+    }
+
+    var ast = parse("40 + 2", "add.js");
+    console.log(compile(ast).toStringWithSourceMap({
+      file: 'add.js'
+    }));
+    // { code: '40 + 2',
+    //   map: [object SourceMapGenerator] }
+
+#### With SourceMapGenerator (low level API)
+
+    var map = new SourceMapGenerator({
+      file: "source-mapped.js"
+    });
+
+    map.addMapping({
+      generated: {
+        line: 10,
+        column: 35
+      },
+      source: "foo.js",
+      original: {
+        line: 33,
+        column: 2
+      },
+      name: "christopher"
+    });
+
+    console.log(map.toString());
+    // '{"version":3,"file":"source-mapped.js","sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'
+
+## API
+
+Get a reference to the module:
+
+    // NodeJS
+    var sourceMap = require('source-map');
+
+    // Browser builds
+    var sourceMap = window.sourceMap;
+
+    // Inside Firefox
+    let sourceMap = {};
+    Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+
+### SourceMapConsumer
+
+A SourceMapConsumer instance represents a parsed source map which we can query
+for information about the original file positions by giving it a file position
+in the generated source.
+
+#### new SourceMapConsumer(rawSourceMap)
+
+The only parameter is the raw source map (either as a string which can be
+`JSON.parse`'d, or an object). According to the spec, source maps have the
+following attributes:
+
+* `version`: Which version of the source map spec this map is following.
+
+* `sources`: An array of URLs to the original source files.
+
+* `names`: An array of identifiers which can be referrenced by individual
+  mappings.
+
+* `sourceRoot`: Optional. The URL root from which all sources are relative.
+
+* `sourcesContent`: Optional. An array of contents of the original source files.
+
+* `mappings`: A string of base64 VLQs which contain the actual mappings.
+
+* `file`: Optional. The generated filename this source map is associated with.
+
+#### SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
+
+Returns the original source, line, and column information for the generated
+source's line and column positions provided. The only argument is an object with
+the following properties:
+
+* `line`: The line number in the generated source.
+
+* `column`: The column number in the generated source.
+
+and an object is returned with the following properties:
+
+* `source`: The original source file, or null if this information is not
+  available.
+
+* `line`: The line number in the original source, or null if this information is
+  not available.
+
+* `column`: The column number in the original source, or null or null if this
+  information is not available.
+
+* `name`: The original identifier, or null if this information is not available.
+
+#### SourceMapConsumer.prototype.generatedPositionFor(originalPosition)
+
+Returns the generated line and column information for the original source,
+line, and column positions provided. The only argument is an object with
+the following properties:
+
+* `source`: The filename of the original source.
+
+* `line`: The line number in the original source.
+
+* `column`: The column number in the original source.
+
+and an object is returned with the following properties:
+
+* `line`: The line number in the generated source, or null.
+
+* `column`: The column number in the generated source, or null.
+
+#### SourceMapConsumer.prototype.sourceContentFor(source)
+
+Returns the original source content for the source provided. The only
+argument is the URL of the original source file.
+
+#### SourceMapConsumer.prototype.eachMapping(callback, context, order)
+
+Iterate over each mapping between an original source/line/column and a
+generated line/column in this source map.
+
+* `callback`: The function that is called with each mapping. Mappings have the
+  form `{ source, generatedLine, generatedColumn, originalLine, originalColumn,
+  name }`
+
+* `context`: Optional. If specified, this object will be the value of `this`
+  every time that `callback` is called.
+
+* `order`: Either `SourceMapConsumer.GENERATED_ORDER` or
+  `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to iterate over
+  the mappings sorted by the generated file's line/column order or the
+  original's source/line/column order, respectively. Defaults to
+  `SourceMapConsumer.GENERATED_ORDER`.
+
+### SourceMapGenerator
+
+An instance of the SourceMapGenerator represents a source map which is being
+built incrementally.
+
+#### new SourceMapGenerator([startOfSourceMap])
+
+You may pass an object with the following properties:
+
+* `file`: The filename of the generated source that this source map is
+  associated with.
+
+* `sourceRoot`: A root for all relative URLs in this source map.
+
+#### SourceMapGenerator.fromSourceMap(sourceMapConsumer)
+
+Creates a new SourceMapGenerator based on a SourceMapConsumer
+
+* `sourceMapConsumer` The SourceMap.
+
+#### SourceMapGenerator.prototype.addMapping(mapping)
+
+Add a single mapping from original source line and column to the generated
+source's line and column for this source map being created. The mapping object
+should have the following properties:
+
+* `generated`: An object with the generated line and column positions.
+
+* `original`: An object with the original line and column positions.
+
+* `source`: The original source file (relative to the sourceRoot).
+
+* `name`: An optional original token name for this mapping.
+
+#### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for an original source file.
+
+* `sourceFile` the URL of the original source file.
+
+* `sourceContent` the content of the source file.
+
+#### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])
+
+Applies a SourceMap for a source file to the SourceMap.
+Each mapping to the supplied source file is rewritten using the
+supplied SourceMap. Note: The resolution for the resulting mappings
+is the minimium of this map and the supplied map.
+
+* `sourceMapConsumer`: The SourceMap to be applied.
+
+* `sourceFile`: Optional. The filename of the source file.
+  If omitted, sourceMapConsumer.file will be used, if it exists.
+  Otherwise an error will be thrown.
+
+* `sourceMapPath`: Optional. The dirname of the path to the SourceMap
+  to be applied. If relative, it is relative to the SourceMap.
+
+  This parameter is needed when the two SourceMaps aren't in the same
+  directory, and the SourceMap to be applied contains relative source
+  paths. If so, those relative source paths need to be rewritten
+  relative to the SourceMap.
+
+  If omitted, it is assumed that both SourceMaps are in the same directory,
+  thus not needing any rewriting. (Supplying `'.'` has the same effect.)
+
+#### SourceMapGenerator.prototype.toString()
+
+Renders the source map being generated to a string.
+
+### SourceNode
+
+SourceNodes provide a way to abstract over interpolating and/or concatenating
+snippets of generated JavaScript source code, while maintaining the line and
+column information associated between those snippets and the original source
+code. This is useful as the final intermediate representation a compiler might
+use before outputting the generated JS and source map.
+
+#### new SourceNode([line, column, source[, chunk[, name]]])
+
+* `line`: The original line number associated with this source node, or null if
+  it isn't associated with an original line.
+
+* `column`: The original column number associated with this source node, or null
+  if it isn't associated with an original column.
+
+* `source`: The original source's filename; null if no filename is provided.
+
+* `chunk`: Optional. Is immediately passed to `SourceNode.prototype.add`, see
+  below.
+
+* `name`: Optional. The original identifier.
+
+#### SourceNode.fromStringWithSourceMap(code, sourceMapConsumer)
+
+Creates a SourceNode from generated code and a SourceMapConsumer.
+
+* `code`: The generated code
+
+* `sourceMapConsumer` The SourceMap for the generated code
+
+#### SourceNode.prototype.add(chunk)
+
+Add a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+   `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.prepend(chunk)
+
+Prepend a chunk of generated JS to this source node.
+
+* `chunk`: A string snippet of generated JS code, another instance of
+   `SourceNode`, or an array where each member is one of those things.
+
+#### SourceNode.prototype.setSourceContent(sourceFile, sourceContent)
+
+Set the source content for a source file. This will be added to the
+`SourceMap` in the `sourcesContent` field.
+
+* `sourceFile`: The filename of the source file
+
+* `sourceContent`: The content of the source file
+
+#### SourceNode.prototype.walk(fn)
+
+Walk over the tree of JS snippets in this node and its children. The walking
+function is called once for each snippet of JS and is passed that snippet and
+the its original associated source's line/column location.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.walkSourceContents(fn)
+
+Walk over the tree of SourceNodes. The walking function is called for each
+source file content and is passed the filename and source content.
+
+* `fn`: The traversal function.
+
+#### SourceNode.prototype.join(sep)
+
+Like `Array.prototype.join` except for SourceNodes. Inserts the separator
+between each of this source node's children.
+
+* `sep`: The separator.
+
+#### SourceNode.prototype.replaceRight(pattern, replacement)
+
+Call `String.prototype.replace` on the very right-most source snippet. Useful
+for trimming whitespace from the end of a source node, etc.
+
+* `pattern`: The pattern to replace.
+
+* `replacement`: The thing to replace the pattern with.
+
+#### SourceNode.prototype.toString()
+
+Return the string representation of this source node. Walks over the tree and
+concatenates all the various snippets together to one string.
+
+### SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])
+
+Returns the string representation of this tree of source nodes, plus a
+SourceMapGenerator which contains all the mappings between the generated and
+original sources.
+
+The arguments are the same as those to `new SourceMapGenerator`.
+
+## Tests
+
+[![Build Status](https://travis-ci.org/mozilla/source-map.png?branch=master)](https://travis-ci.org/mozilla/source-map)
+
+Install NodeJS version 0.8.0 or greater, then run `node test/run-tests.js`.
+
+To add new tests, create a new file named `test/test-<your new test name>.js`
+and export your test functions with names that start with "test", for example
+
+    exports["test doing the foo bar"] = function (assert, util) {
+      ...
+    };
+
+The new test will be located automatically when you run the suite.
+
+The `util` argument is the test utility module located at `test/source-map/util`.
+
+The `assert` argument is a cut down version of node's assert module. You have
+access to the following assertion functions:
+
+* `doesNotThrow`
+
+* `equal`
+
+* `ok`
+
+* `strictEqual`
+
+* `throws`
+
+(The reason for the restricted set of test functions is because we need the
+tests to run inside Firefox's test suite as well and so the assert module is
+shimmed in that environment. See `build/assert-shim.js`.)
+
+[format]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+[feature]: https://wiki.mozilla.org/DevTools/Features/SourceMap
+[Dryice]: https://github.com/mozilla/dryice
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/assert-shim.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/assert-shim.js
new file mode 100644
index 0000000..daa1a62
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/assert-shim.js
@@ -0,0 +1,56 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+define('test/source-map/assert', ['exports'], function (exports) {
+
+  let do_throw = function (msg) {
+    throw new Error(msg);
+  };
+
+  exports.init = function (throw_fn) {
+    do_throw = throw_fn;
+  };
+
+  exports.doesNotThrow = function (fn) {
+    try {
+      fn();
+    }
+    catch (e) {
+      do_throw(e.message);
+    }
+  };
+
+  exports.equal = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' != ' + String(expected);
+    if (actual != expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.ok = function (val, msg) {
+    msg = msg || String(val) + ' is falsey';
+    if (!Boolean(val)) {
+      do_throw(msg);
+    }
+  };
+
+  exports.strictEqual = function (actual, expected, msg) {
+    msg = msg || String(actual) + ' !== ' + String(expected);
+    if (actual !== expected) {
+      do_throw(msg);
+    }
+  };
+
+  exports.throws = function (fn) {
+    try {
+      fn();
+      do_throw('Expected an error to be thrown, but it wasn\'t.');
+    }
+    catch (e) {
+    }
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/mini-require.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/mini-require.js
new file mode 100644
index 0000000..0daf453
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/mini-require.js
@@ -0,0 +1,152 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/**
+ * Define a module along with a payload.
+ * @param {string} moduleName Name for the payload
+ * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
+ * @param {function} payload Function with (require, exports, module) params
+ */
+function define(moduleName, deps, payload) {
+  if (typeof moduleName != "string") {
+    throw new TypeError('Expected string, got: ' + moduleName);
+  }
+
+  if (arguments.length == 2) {
+    payload = deps;
+  }
+
+  if (moduleName in define.modules) {
+    throw new Error("Module already defined: " + moduleName);
+  }
+  define.modules[moduleName] = payload;
+};
+
+/**
+ * The global store of un-instantiated modules
+ */
+define.modules = {};
+
+
+/**
+ * We invoke require() in the context of a Domain so we can have multiple
+ * sets of modules running separate from each other.
+ * This contrasts with JSMs which are singletons, Domains allows us to
+ * optionally load a CommonJS module twice with separate data each time.
+ * Perhaps you want 2 command lines with a different set of commands in each,
+ * for example.
+ */
+function Domain() {
+  this.modules = {};
+  this._currentModule = null;
+}
+
+(function () {
+
+  /**
+   * Lookup module names and resolve them by calling the definition function if
+   * needed.
+   * There are 2 ways to call this, either with an array of dependencies and a
+   * callback to call when the dependencies are found (which can happen
+   * asynchronously in an in-page context) or with a single string an no callback
+   * where the dependency is resolved synchronously and returned.
+   * The API is designed to be compatible with the CommonJS AMD spec and
+   * RequireJS.
+   * @param {string[]|string} deps A name, or names for the payload
+   * @param {function|undefined} callback Function to call when the dependencies
+   * are resolved
+   * @return {undefined|object} The module required or undefined for
+   * array/callback method
+   */
+  Domain.prototype.require = function(deps, callback) {
+    if (Array.isArray(deps)) {
+      var params = deps.map(function(dep) {
+        return this.lookup(dep);
+      }, this);
+      if (callback) {
+        callback.apply(null, params);
+      }
+      return undefined;
+    }
+    else {
+      return this.lookup(deps);
+    }
+  };
+
+  function normalize(path) {
+    var bits = path.split('/');
+    var i = 1;
+    while (i < bits.length) {
+      if (bits[i] === '..') {
+        bits.splice(i-1, 1);
+      } else if (bits[i] === '.') {
+        bits.splice(i, 1);
+      } else {
+        i++;
+      }
+    }
+    return bits.join('/');
+  }
+
+  function join(a, b) {
+    a = a.trim();
+    b = b.trim();
+    if (/^\//.test(b)) {
+      return b;
+    } else {
+      return a.replace(/\/*$/, '/') + b;
+    }
+  }
+
+  function dirname(path) {
+    var bits = path.split('/');
+    bits.pop();
+    return bits.join('/');
+  }
+
+  /**
+   * Lookup module names and resolve them by calling the definition function if
+   * needed.
+   * @param {string} moduleName A name for the payload to lookup
+   * @return {object} The module specified by aModuleName or null if not found.
+   */
+  Domain.prototype.lookup = function(moduleName) {
+    if (/^\./.test(moduleName)) {
+      moduleName = normalize(join(dirname(this._currentModule), moduleName));
+    }
+
+    if (moduleName in this.modules) {
+      var module = this.modules[moduleName];
+      return module;
+    }
+
+    if (!(moduleName in define.modules)) {
+      throw new Error("Module not defined: " + moduleName);
+    }
+
+    var module = define.modules[moduleName];
+
+    if (typeof module == "function") {
+      var exports = {};
+      var previousModule = this._currentModule;
+      this._currentModule = moduleName;
+      module(this.require.bind(this), exports, { id: moduleName, uri: "" });
+      this._currentModule = previousModule;
+      module = exports;
+    }
+
+    // cache the resulting module object for next time
+    this.modules[moduleName] = module;
+
+    return module;
+  };
+
+}());
+
+define.Domain = Domain;
+define.globalDomain = new Domain();
+var require = define.globalDomain.require.bind(define.globalDomain);
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/prefix-source-map.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/prefix-source-map.jsm
new file mode 100644
index 0000000..ee2539d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/prefix-source-map.jsm
@@ -0,0 +1,20 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+this.EXPORTED_SYMBOLS = [ "SourceMapConsumer", "SourceMapGenerator", "SourceNode" ];
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/prefix-utils.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/prefix-utils.jsm
new file mode 100644
index 0000000..80341d4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/prefix-utils.jsm
@@ -0,0 +1,18 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://gre/modules/devtools/Require.jsm');
+Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
+
+this.EXPORTED_SYMBOLS = [ "define", "runSourceMapTests" ];
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-browser.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-browser.js
new file mode 100644
index 0000000..fb29ff5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-browser.js
@@ -0,0 +1,8 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.sourceMap = {
+  SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer,
+  SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator,
+  SourceNode: require('source-map/source-node').SourceNode
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-source-map.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-source-map.jsm
new file mode 100644
index 0000000..cf3c2d8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-source-map.jsm
@@ -0,0 +1,6 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+///////////////////////////////////////////////////////////////////////////////
+
+this.SourceMapConsumer = require('source-map/source-map-consumer').SourceMapConsumer;
+this.SourceMapGenerator = require('source-map/source-map-generator').SourceMapGenerator;
+this.SourceNode = require('source-map/source-node').SourceNode;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-utils.jsm b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-utils.jsm
new file mode 100644
index 0000000..b31b84c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/suffix-utils.jsm
@@ -0,0 +1,21 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+function runSourceMapTests(modName, do_throw) {
+  let mod = require(modName);
+  let assert = require('test/source-map/assert');
+  let util = require('test/source-map/util');
+
+  assert.init(do_throw);
+
+  for (let k in mod) {
+    if (/^test/.test(k)) {
+      mod[k](assert, util);
+    }
+  }
+
+}
+this.runSourceMapTests = runSourceMapTests;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/test-prefix.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/test-prefix.js
new file mode 100644
index 0000000..1b13f30
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/test-prefix.js
@@ -0,0 +1,8 @@
+/*
+ * WARNING!
+ *
+ * Do not edit this file directly, it is built from the sources at
+ * https://github.com/mozilla/source-map/
+ */
+
+Components.utils.import('resource://test/Utils.jsm');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/test-suffix.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/test-suffix.js
new file mode 100644
index 0000000..bec2de3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/build/test-suffix.js
@@ -0,0 +1,3 @@
+function run_test() {
+  runSourceMapTests('{THIS_MODULE}', do_throw);
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map.js
new file mode 100644
index 0000000..121ad24
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map.js
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
+exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
+exports.SourceNode = require('./source-map/source-node').SourceNode;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/array-set.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/array-set.js
new file mode 100644
index 0000000..40f9a18
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/array-set.js
@@ -0,0 +1,97 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+
+  /**
+   * A data structure which is a combination of an array and a set. Adding a new
+   * member is O(1), testing for membership is O(1), and finding the index of an
+   * element is O(1). Removing elements from the set is not supported. Only
+   * strings are supported for membership.
+   */
+  function ArraySet() {
+    this._array = [];
+    this._set = {};
+  }
+
+  /**
+   * Static method for creating ArraySet instances from an existing array.
+   */
+  ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
+    var set = new ArraySet();
+    for (var i = 0, len = aArray.length; i < len; i++) {
+      set.add(aArray[i], aAllowDuplicates);
+    }
+    return set;
+  };
+
+  /**
+   * Add the given string to this set.
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
+    var isDuplicate = this.has(aStr);
+    var idx = this._array.length;
+    if (!isDuplicate || aAllowDuplicates) {
+      this._array.push(aStr);
+    }
+    if (!isDuplicate) {
+      this._set[util.toSetString(aStr)] = idx;
+    }
+  };
+
+  /**
+   * Is the given string a member of this set?
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.has = function ArraySet_has(aStr) {
+    return Object.prototype.hasOwnProperty.call(this._set,
+                                                util.toSetString(aStr));
+  };
+
+  /**
+   * What is the index of the given string in the array?
+   *
+   * @param String aStr
+   */
+  ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+    if (this.has(aStr)) {
+      return this._set[util.toSetString(aStr)];
+    }
+    throw new Error('"' + aStr + '" is not in the set.');
+  };
+
+  /**
+   * What is the element at the given index?
+   *
+   * @param Number aIdx
+   */
+  ArraySet.prototype.at = function ArraySet_at(aIdx) {
+    if (aIdx >= 0 && aIdx < this._array.length) {
+      return this._array[aIdx];
+    }
+    throw new Error('No element indexed by ' + aIdx);
+  };
+
+  /**
+   * Returns the array representation of this set (which has the proper indices
+   * indicated by indexOf). Note that this is a copy of the internal array used
+   * for storing the members so that no one can mess with internal state.
+   */
+  ArraySet.prototype.toArray = function ArraySet_toArray() {
+    return this._array.slice();
+  };
+
+  exports.ArraySet = ArraySet;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/base64-vlq.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/base64-vlq.js
new file mode 100644
index 0000000..1b67bb3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/base64-vlq.js
@@ -0,0 +1,144 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ *  * Neither the name of Google Inc. nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64 = require('./base64');
+
+  // A single base 64 digit can contain 6 bits of data. For the base 64 variable
+  // length quantities we use in the source map spec, the first bit is the sign,
+  // the next four bits are the actual value, and the 6th bit is the
+  // continuation bit. The continuation bit tells us whether there are more
+  // digits in this value following this digit.
+  //
+  //   Continuation
+  //   |    Sign
+  //   |    |
+  //   V    V
+  //   101011
+
+  var VLQ_BASE_SHIFT = 5;
+
+  // binary: 100000
+  var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+  // binary: 011111
+  var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+  // binary: 100000
+  var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+  /**
+   * Converts from a two-complement value to a value where the sign bit is
+   * is placed in the least significant bit.  For example, as decimals:
+   *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+   *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+   */
+  function toVLQSigned(aValue) {
+    return aValue < 0
+      ? ((-aValue) << 1) + 1
+      : (aValue << 1) + 0;
+  }
+
+  /**
+   * Converts to a two-complement value from a value where the sign bit is
+   * is placed in the least significant bit.  For example, as decimals:
+   *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+   *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+   */
+  function fromVLQSigned(aValue) {
+    var isNegative = (aValue & 1) === 1;
+    var shifted = aValue >> 1;
+    return isNegative
+      ? -shifted
+      : shifted;
+  }
+
+  /**
+   * Returns the base 64 VLQ encoded value.
+   */
+  exports.encode = function base64VLQ_encode(aValue) {
+    var encoded = "";
+    var digit;
+
+    var vlq = toVLQSigned(aValue);
+
+    do {
+      digit = vlq & VLQ_BASE_MASK;
+      vlq >>>= VLQ_BASE_SHIFT;
+      if (vlq > 0) {
+        // There are still more digits in this value, so we must make sure the
+        // continuation bit is marked.
+        digit |= VLQ_CONTINUATION_BIT;
+      }
+      encoded += base64.encode(digit);
+    } while (vlq > 0);
+
+    return encoded;
+  };
+
+  /**
+   * Decodes the next base 64 VLQ value from the given string and returns the
+   * value and the rest of the string.
+   */
+  exports.decode = function base64VLQ_decode(aStr) {
+    var i = 0;
+    var strLen = aStr.length;
+    var result = 0;
+    var shift = 0;
+    var continuation, digit;
+
+    do {
+      if (i >= strLen) {
+        throw new Error("Expected more digits in base 64 VLQ value.");
+      }
+      digit = base64.decode(aStr.charAt(i++));
+      continuation = !!(digit & VLQ_CONTINUATION_BIT);
+      digit &= VLQ_BASE_MASK;
+      result = result + (digit << shift);
+      shift += VLQ_BASE_SHIFT;
+    } while (continuation);
+
+    return {
+      value: fromVLQSigned(result),
+      rest: aStr.slice(i)
+    };
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/base64.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/base64.js
new file mode 100644
index 0000000..863cc46
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/base64.js
@@ -0,0 +1,42 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var charToIntMap = {};
+  var intToCharMap = {};
+
+  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+    .split('')
+    .forEach(function (ch, index) {
+      charToIntMap[ch] = index;
+      intToCharMap[index] = ch;
+    });
+
+  /**
+   * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+   */
+  exports.encode = function base64_encode(aNumber) {
+    if (aNumber in intToCharMap) {
+      return intToCharMap[aNumber];
+    }
+    throw new TypeError("Must be between 0 and 63: " + aNumber);
+  };
+
+  /**
+   * Decode a single base 64 digit to an integer.
+   */
+  exports.decode = function base64_decode(aChar) {
+    if (aChar in charToIntMap) {
+      return charToIntMap[aChar];
+    }
+    throw new TypeError("Not a valid base 64 digit: " + aChar);
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/binary-search.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/binary-search.js
new file mode 100644
index 0000000..ff347c6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/binary-search.js
@@ -0,0 +1,81 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  /**
+   * Recursive implementation of binary search.
+   *
+   * @param aLow Indices here and lower do not contain the needle.
+   * @param aHigh Indices here and higher do not contain the needle.
+   * @param aNeedle The element being searched for.
+   * @param aHaystack The non-empty array being searched.
+   * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+   */
+  function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
+    // This function terminates when one of the following is true:
+    //
+    //   1. We find the exact element we are looking for.
+    //
+    //   2. We did not find the exact element, but we can return the next
+    //      closest element that is less than that element.
+    //
+    //   3. We did not find the exact element, and there is no next-closest
+    //      element which is less than the one we are searching for, so we
+    //      return null.
+    var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+    var cmp = aCompare(aNeedle, aHaystack[mid], true);
+    if (cmp === 0) {
+      // Found the element we are looking for.
+      return aHaystack[mid];
+    }
+    else if (cmp > 0) {
+      // aHaystack[mid] is greater than our needle.
+      if (aHigh - mid > 1) {
+        // The element is in the upper half.
+        return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
+      }
+      // We did not find an exact match, return the next closest one
+      // (termination case 2).
+      return aHaystack[mid];
+    }
+    else {
+      // aHaystack[mid] is less than our needle.
+      if (mid - aLow > 1) {
+        // The element is in the lower half.
+        return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
+      }
+      // The exact needle element was not found in this haystack. Determine if
+      // we are in termination case (2) or (3) and return the appropriate thing.
+      return aLow < 0
+        ? null
+        : aHaystack[aLow];
+    }
+  }
+
+  /**
+   * This is an implementation of binary search which will always try and return
+   * the next lowest value checked if there is no exact hit. This is because
+   * mappings between original and generated line/col pairs are single points,
+   * and there is an implicit region between each of them, so a miss just means
+   * that you aren't on the very start of a region.
+   *
+   * @param aNeedle The element you are looking for.
+   * @param aHaystack The array that is being searched.
+   * @param aCompare A function which takes the needle and an element in the
+   *     array and returns -1, 0, or 1 depending on whether the needle is less
+   *     than, equal to, or greater than the element, respectively.
+   */
+  exports.search = function search(aNeedle, aHaystack, aCompare) {
+    return aHaystack.length > 0
+      ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
+      : null;
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-map-consumer.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-map-consumer.js
new file mode 100644
index 0000000..5214d5e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-map-consumer.js
@@ -0,0 +1,478 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('./util');
+  var binarySearch = require('./binary-search');
+  var ArraySet = require('./array-set').ArraySet;
+  var base64VLQ = require('./base64-vlq');
+
+  /**
+   * A SourceMapConsumer instance represents a parsed source map which we can
+   * query for information about the original file positions by giving it a file
+   * position in the generated source.
+   *
+   * The only parameter is the raw source map (either as a JSON string, or
+   * already parsed to an object). According to the spec, source maps have the
+   * following attributes:
+   *
+   *   - version: Which version of the source map spec this map is following.
+   *   - sources: An array of URLs to the original source files.
+   *   - names: An array of identifiers which can be referrenced by individual mappings.
+   *   - sourceRoot: Optional. The URL root from which all sources are relative.
+   *   - sourcesContent: Optional. An array of contents of the original source files.
+   *   - mappings: A string of base64 VLQs which contain the actual mappings.
+   *   - file: Optional. The generated file this source map is associated with.
+   *
+   * Here is an example source map, taken from the source map spec[0]:
+   *
+   *     {
+   *       version : 3,
+   *       file: "out.js",
+   *       sourceRoot : "",
+   *       sources: ["foo.js", "bar.js"],
+   *       names: ["src", "maps", "are", "fun"],
+   *       mappings: "AA,AB;;ABCDE;"
+   *     }
+   *
+   * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+   */
+  function SourceMapConsumer(aSourceMap) {
+    var sourceMap = aSourceMap;
+    if (typeof aSourceMap === 'string') {
+      sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+    }
+
+    var version = util.getArg(sourceMap, 'version');
+    var sources = util.getArg(sourceMap, 'sources');
+    // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
+    // requires the array) to play nice here.
+    var names = util.getArg(sourceMap, 'names', []);
+    var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+    var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
+    var mappings = util.getArg(sourceMap, 'mappings');
+    var file = util.getArg(sourceMap, 'file', null);
+
+    // Once again, Sass deviates from the spec and supplies the version as a
+    // string rather than a number, so we use loose equality checking here.
+    if (version != this._version) {
+      throw new Error('Unsupported version: ' + version);
+    }
+
+    // Pass `true` below to allow duplicate names and sources. While source maps
+    // are intended to be compressed and deduplicated, the TypeScript compiler
+    // sometimes generates source maps with duplicates in them. See Github issue
+    // #72 and bugzil.la/889492.
+    this._names = ArraySet.fromArray(names, true);
+    this._sources = ArraySet.fromArray(sources, true);
+
+    this.sourceRoot = sourceRoot;
+    this.sourcesContent = sourcesContent;
+    this._mappings = mappings;
+    this.file = file;
+  }
+
+  /**
+   * Create a SourceMapConsumer from a SourceMapGenerator.
+   *
+   * @param SourceMapGenerator aSourceMap
+   *        The source map that will be consumed.
+   * @returns SourceMapConsumer
+   */
+  SourceMapConsumer.fromSourceMap =
+    function SourceMapConsumer_fromSourceMap(aSourceMap) {
+      var smc = Object.create(SourceMapConsumer.prototype);
+
+      smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
+      smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
+      smc.sourceRoot = aSourceMap._sourceRoot;
+      smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
+                                                              smc.sourceRoot);
+      smc.file = aSourceMap._file;
+
+      smc.__generatedMappings = aSourceMap._mappings.slice()
+        .sort(util.compareByGeneratedPositions);
+      smc.__originalMappings = aSourceMap._mappings.slice()
+        .sort(util.compareByOriginalPositions);
+
+      return smc;
+    };
+
+  /**
+   * The version of the source mapping spec that we are consuming.
+   */
+  SourceMapConsumer.prototype._version = 3;
+
+  /**
+   * The list of original sources.
+   */
+  Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
+    get: function () {
+      return this._sources.toArray().map(function (s) {
+        return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
+      }, this);
+    }
+  });
+
+  // `__generatedMappings` and `__originalMappings` are arrays that hold the
+  // parsed mapping coordinates from the source map's "mappings" attribute. They
+  // are lazily instantiated, accessed via the `_generatedMappings` and
+  // `_originalMappings` getters respectively, and we only parse the mappings
+  // and create these arrays once queried for a source location. We jump through
+  // these hoops because there can be many thousands of mappings, and parsing
+  // them is expensive, so we only want to do it if we must.
+  //
+  // Each object in the arrays is of the form:
+  //
+  //     {
+  //       generatedLine: The line number in the generated code,
+  //       generatedColumn: The column number in the generated code,
+  //       source: The path to the original source file that generated this
+  //               chunk of code,
+  //       originalLine: The line number in the original source that
+  //                     corresponds to this chunk of generated code,
+  //       originalColumn: The column number in the original source that
+  //                       corresponds to this chunk of generated code,
+  //       name: The name of the original symbol which generated this chunk of
+  //             code.
+  //     }
+  //
+  // All properties except for `generatedLine` and `generatedColumn` can be
+  // `null`.
+  //
+  // `_generatedMappings` is ordered by the generated positions.
+  //
+  // `_originalMappings` is ordered by the original positions.
+
+  SourceMapConsumer.prototype.__generatedMappings = null;
+  Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
+    get: function () {
+      if (!this.__generatedMappings) {
+        this.__generatedMappings = [];
+        this.__originalMappings = [];
+        this._parseMappings(this._mappings, this.sourceRoot);
+      }
+
+      return this.__generatedMappings;
+    }
+  });
+
+  SourceMapConsumer.prototype.__originalMappings = null;
+  Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
+    get: function () {
+      if (!this.__originalMappings) {
+        this.__generatedMappings = [];
+        this.__originalMappings = [];
+        this._parseMappings(this._mappings, this.sourceRoot);
+      }
+
+      return this.__originalMappings;
+    }
+  });
+
+  /**
+   * Parse the mappings in a string in to a data structure which we can easily
+   * query (the ordered arrays in the `this.__generatedMappings` and
+   * `this.__originalMappings` properties).
+   */
+  SourceMapConsumer.prototype._parseMappings =
+    function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+      var generatedLine = 1;
+      var previousGeneratedColumn = 0;
+      var previousOriginalLine = 0;
+      var previousOriginalColumn = 0;
+      var previousSource = 0;
+      var previousName = 0;
+      var mappingSeparator = /^[,;]/;
+      var str = aStr;
+      var mapping;
+      var temp;
+
+      while (str.length > 0) {
+        if (str.charAt(0) === ';') {
+          generatedLine++;
+          str = str.slice(1);
+          previousGeneratedColumn = 0;
+        }
+        else if (str.charAt(0) === ',') {
+          str = str.slice(1);
+        }
+        else {
+          mapping = {};
+          mapping.generatedLine = generatedLine;
+
+          // Generated column.
+          temp = base64VLQ.decode(str);
+          mapping.generatedColumn = previousGeneratedColumn + temp.value;
+          previousGeneratedColumn = mapping.generatedColumn;
+          str = temp.rest;
+
+          if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+            // Original source.
+            temp = base64VLQ.decode(str);
+            mapping.source = this._sources.at(previousSource + temp.value);
+            previousSource += temp.value;
+            str = temp.rest;
+            if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+              throw new Error('Found a source, but no line and column');
+            }
+
+            // Original line.
+            temp = base64VLQ.decode(str);
+            mapping.originalLine = previousOriginalLine + temp.value;
+            previousOriginalLine = mapping.originalLine;
+            // Lines are stored 0-based
+            mapping.originalLine += 1;
+            str = temp.rest;
+            if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+              throw new Error('Found a source and line, but no column');
+            }
+
+            // Original column.
+            temp = base64VLQ.decode(str);
+            mapping.originalColumn = previousOriginalColumn + temp.value;
+            previousOriginalColumn = mapping.originalColumn;
+            str = temp.rest;
+
+            if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+              // Original name.
+              temp = base64VLQ.decode(str);
+              mapping.name = this._names.at(previousName + temp.value);
+              previousName += temp.value;
+              str = temp.rest;
+            }
+          }
+
+          this.__generatedMappings.push(mapping);
+          if (typeof mapping.originalLine === 'number') {
+            this.__originalMappings.push(mapping);
+          }
+        }
+      }
+
+      this.__generatedMappings.sort(util.compareByGeneratedPositions);
+      this.__originalMappings.sort(util.compareByOriginalPositions);
+    };
+
+  /**
+   * Find the mapping that best matches the hypothetical "needle" mapping that
+   * we are searching for in the given "haystack" of mappings.
+   */
+  SourceMapConsumer.prototype._findMapping =
+    function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
+                                           aColumnName, aComparator) {
+      // To return the position we are searching for, we must first find the
+      // mapping for the given position and then return the opposite position it
+      // points to. Because the mappings are sorted, we can use binary search to
+      // find the best mapping.
+
+      if (aNeedle[aLineName] <= 0) {
+        throw new TypeError('Line must be greater than or equal to 1, got '
+                            + aNeedle[aLineName]);
+      }
+      if (aNeedle[aColumnName] < 0) {
+        throw new TypeError('Column must be greater than or equal to 0, got '
+                            + aNeedle[aColumnName]);
+      }
+
+      return binarySearch.search(aNeedle, aMappings, aComparator);
+    };
+
+  /**
+   * Returns the original source, line, and column information for the generated
+   * source's line and column positions provided. The only argument is an object
+   * with the following properties:
+   *
+   *   - line: The line number in the generated source.
+   *   - column: The column number in the generated source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - source: The original source file, or null.
+   *   - line: The line number in the original source, or null.
+   *   - column: The column number in the original source, or null.
+   *   - name: The original identifier, or null.
+   */
+  SourceMapConsumer.prototype.originalPositionFor =
+    function SourceMapConsumer_originalPositionFor(aArgs) {
+      var needle = {
+        generatedLine: util.getArg(aArgs, 'line'),
+        generatedColumn: util.getArg(aArgs, 'column')
+      };
+
+      var mapping = this._findMapping(needle,
+                                      this._generatedMappings,
+                                      "generatedLine",
+                                      "generatedColumn",
+                                      util.compareByGeneratedPositions);
+
+      if (mapping && mapping.generatedLine === needle.generatedLine) {
+        var source = util.getArg(mapping, 'source', null);
+        if (source && this.sourceRoot) {
+          source = util.join(this.sourceRoot, source);
+        }
+        return {
+          source: source,
+          line: util.getArg(mapping, 'originalLine', null),
+          column: util.getArg(mapping, 'originalColumn', null),
+          name: util.getArg(mapping, 'name', null)
+        };
+      }
+
+      return {
+        source: null,
+        line: null,
+        column: null,
+        name: null
+      };
+    };
+
+  /**
+   * Returns the original source content. The only argument is the url of the
+   * original source file. Returns null if no original source content is
+   * availible.
+   */
+  SourceMapConsumer.prototype.sourceContentFor =
+    function SourceMapConsumer_sourceContentFor(aSource) {
+      if (!this.sourcesContent) {
+        return null;
+      }
+
+      if (this.sourceRoot) {
+        aSource = util.relative(this.sourceRoot, aSource);
+      }
+
+      if (this._sources.has(aSource)) {
+        return this.sourcesContent[this._sources.indexOf(aSource)];
+      }
+
+      var url;
+      if (this.sourceRoot
+          && (url = util.urlParse(this.sourceRoot))) {
+        // XXX: file:// URIs and absolute paths lead to unexpected behavior for
+        // many users. We can help them out when they expect file:// URIs to
+        // behave like it would if they were running a local HTTP server. See
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
+        var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+        if (url.scheme == "file"
+            && this._sources.has(fileUriAbsPath)) {
+          return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
+        }
+
+        if ((!url.path || url.path == "/")
+            && this._sources.has("/" + aSource)) {
+          return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+        }
+      }
+
+      throw new Error('"' + aSource + '" is not in the SourceMap.');
+    };
+
+  /**
+   * Returns the generated line and column information for the original source,
+   * line, and column positions provided. The only argument is an object with
+   * the following properties:
+   *
+   *   - source: The filename of the original source.
+   *   - line: The line number in the original source.
+   *   - column: The column number in the original source.
+   *
+   * and an object is returned with the following properties:
+   *
+   *   - line: The line number in the generated source, or null.
+   *   - column: The column number in the generated source, or null.
+   */
+  SourceMapConsumer.prototype.generatedPositionFor =
+    function SourceMapConsumer_generatedPositionFor(aArgs) {
+      var needle = {
+        source: util.getArg(aArgs, 'source'),
+        originalLine: util.getArg(aArgs, 'line'),
+        originalColumn: util.getArg(aArgs, 'column')
+      };
+
+      if (this.sourceRoot) {
+        needle.source = util.relative(this.sourceRoot, needle.source);
+      }
+
+      var mapping = this._findMapping(needle,
+                                      this._originalMappings,
+                                      "originalLine",
+                                      "originalColumn",
+                                      util.compareByOriginalPositions);
+
+      if (mapping) {
+        return {
+          line: util.getArg(mapping, 'generatedLine', null),
+          column: util.getArg(mapping, 'generatedColumn', null)
+        };
+      }
+
+      return {
+        line: null,
+        column: null
+      };
+    };
+
+  SourceMapConsumer.GENERATED_ORDER = 1;
+  SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+  /**
+   * Iterate over each mapping between an original source/line/column and a
+   * generated line/column in this source map.
+   *
+   * @param Function aCallback
+   *        The function that is called with each mapping.
+   * @param Object aContext
+   *        Optional. If specified, this object will be the value of `this` every
+   *        time that `aCallback` is called.
+   * @param aOrder
+   *        Either `SourceMapConsumer.GENERATED_ORDER` or
+   *        `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+   *        iterate over the mappings sorted by the generated file's line/column
+   *        order or the original's source/line/column order, respectively. Defaults to
+   *        `SourceMapConsumer.GENERATED_ORDER`.
+   */
+  SourceMapConsumer.prototype.eachMapping =
+    function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+      var context = aContext || null;
+      var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+      var mappings;
+      switch (order) {
+      case SourceMapConsumer.GENERATED_ORDER:
+        mappings = this._generatedMappings;
+        break;
+      case SourceMapConsumer.ORIGINAL_ORDER:
+        mappings = this._originalMappings;
+        break;
+      default:
+        throw new Error("Unknown order of iteration.");
+      }
+
+      var sourceRoot = this.sourceRoot;
+      mappings.map(function (mapping) {
+        var source = mapping.source;
+        if (source && sourceRoot) {
+          source = util.join(sourceRoot, source);
+        }
+        return {
+          source: source,
+          generatedLine: mapping.generatedLine,
+          generatedColumn: mapping.generatedColumn,
+          originalLine: mapping.originalLine,
+          originalColumn: mapping.originalColumn,
+          name: mapping.name
+        };
+      }).forEach(aCallback, context);
+    };
+
+  exports.SourceMapConsumer = SourceMapConsumer;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-map-generator.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-map-generator.js
new file mode 100644
index 0000000..fb6d6c3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-map-generator.js
@@ -0,0 +1,400 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64VLQ = require('./base64-vlq');
+  var util = require('./util');
+  var ArraySet = require('./array-set').ArraySet;
+
+  /**
+   * An instance of the SourceMapGenerator represents a source map which is
+   * being built incrementally. You may pass an object with the following
+   * properties:
+   *
+   *   - file: The filename of the generated source.
+   *   - sourceRoot: A root for all relative URLs in this source map.
+   */
+  function SourceMapGenerator(aArgs) {
+    if (!aArgs) {
+      aArgs = {};
+    }
+    this._file = util.getArg(aArgs, 'file', null);
+    this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+    this._sources = new ArraySet();
+    this._names = new ArraySet();
+    this._mappings = [];
+    this._sourcesContents = null;
+  }
+
+  SourceMapGenerator.prototype._version = 3;
+
+  /**
+   * Creates a new SourceMapGenerator based on a SourceMapConsumer
+   *
+   * @param aSourceMapConsumer The SourceMap.
+   */
+  SourceMapGenerator.fromSourceMap =
+    function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
+      var sourceRoot = aSourceMapConsumer.sourceRoot;
+      var generator = new SourceMapGenerator({
+        file: aSourceMapConsumer.file,
+        sourceRoot: sourceRoot
+      });
+      aSourceMapConsumer.eachMapping(function (mapping) {
+        var newMapping = {
+          generated: {
+            line: mapping.generatedLine,
+            column: mapping.generatedColumn
+          }
+        };
+
+        if (mapping.source) {
+          newMapping.source = mapping.source;
+          if (sourceRoot) {
+            newMapping.source = util.relative(sourceRoot, newMapping.source);
+          }
+
+          newMapping.original = {
+            line: mapping.originalLine,
+            column: mapping.originalColumn
+          };
+
+          if (mapping.name) {
+            newMapping.name = mapping.name;
+          }
+        }
+
+        generator.addMapping(newMapping);
+      });
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content) {
+          generator.setSourceContent(sourceFile, content);
+        }
+      });
+      return generator;
+    };
+
+  /**
+   * Add a single mapping from original source line and column to the generated
+   * source's line and column for this source map being created. The mapping
+   * object should have the following properties:
+   *
+   *   - generated: An object with the generated line and column positions.
+   *   - original: An object with the original line and column positions.
+   *   - source: The original source file (relative to the sourceRoot).
+   *   - name: An optional original token name for this mapping.
+   */
+  SourceMapGenerator.prototype.addMapping =
+    function SourceMapGenerator_addMapping(aArgs) {
+      var generated = util.getArg(aArgs, 'generated');
+      var original = util.getArg(aArgs, 'original', null);
+      var source = util.getArg(aArgs, 'source', null);
+      var name = util.getArg(aArgs, 'name', null);
+
+      this._validateMapping(generated, original, source, name);
+
+      if (source && !this._sources.has(source)) {
+        this._sources.add(source);
+      }
+
+      if (name && !this._names.has(name)) {
+        this._names.add(name);
+      }
+
+      this._mappings.push({
+        generatedLine: generated.line,
+        generatedColumn: generated.column,
+        originalLine: original != null && original.line,
+        originalColumn: original != null && original.column,
+        source: source,
+        name: name
+      });
+    };
+
+  /**
+   * Set the source content for a source file.
+   */
+  SourceMapGenerator.prototype.setSourceContent =
+    function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
+      var source = aSourceFile;
+      if (this._sourceRoot) {
+        source = util.relative(this._sourceRoot, source);
+      }
+
+      if (aSourceContent !== null) {
+        // Add the source content to the _sourcesContents map.
+        // Create a new _sourcesContents map if the property is null.
+        if (!this._sourcesContents) {
+          this._sourcesContents = {};
+        }
+        this._sourcesContents[util.toSetString(source)] = aSourceContent;
+      } else {
+        // Remove the source file from the _sourcesContents map.
+        // If the _sourcesContents map is empty, set the property to null.
+        delete this._sourcesContents[util.toSetString(source)];
+        if (Object.keys(this._sourcesContents).length === 0) {
+          this._sourcesContents = null;
+        }
+      }
+    };
+
+  /**
+   * Applies the mappings of a sub-source-map for a specific source file to the
+   * source map being generated. Each mapping to the supplied source file is
+   * rewritten using the supplied source map. Note: The resolution for the
+   * resulting mappings is the minimium of this map and the supplied map.
+   *
+   * @param aSourceMapConsumer The source map to be applied.
+   * @param aSourceFile Optional. The filename of the source file.
+   *        If omitted, SourceMapConsumer's file property will be used.
+   * @param aSourceMapPath Optional. The dirname of the path to the source map
+   *        to be applied. If relative, it is relative to the SourceMapConsumer.
+   *        This parameter is needed when the two source maps aren't in the same
+   *        directory, and the source map to be applied contains relative source
+   *        paths. If so, those relative source paths need to be rewritten
+   *        relative to the SourceMapGenerator.
+   */
+  SourceMapGenerator.prototype.applySourceMap =
+    function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
+      // If aSourceFile is omitted, we will use the file property of the SourceMap
+      if (!aSourceFile) {
+        if (!aSourceMapConsumer.file) {
+          throw new Error(
+            'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
+            'or the source map\'s "file" property. Both were omitted.'
+          );
+        }
+        aSourceFile = aSourceMapConsumer.file;
+      }
+      var sourceRoot = this._sourceRoot;
+      // Make "aSourceFile" relative if an absolute Url is passed.
+      if (sourceRoot) {
+        aSourceFile = util.relative(sourceRoot, aSourceFile);
+      }
+      // Applying the SourceMap can add and remove items from the sources and
+      // the names array.
+      var newSources = new ArraySet();
+      var newNames = new ArraySet();
+
+      // Find mappings for the "aSourceFile"
+      this._mappings.forEach(function (mapping) {
+        if (mapping.source === aSourceFile && mapping.originalLine) {
+          // Check if it can be mapped by the source map, then update the mapping.
+          var original = aSourceMapConsumer.originalPositionFor({
+            line: mapping.originalLine,
+            column: mapping.originalColumn
+          });
+          if (original.source !== null) {
+            // Copy mapping
+            mapping.source = original.source;
+            if (aSourceMapPath) {
+              mapping.source = util.join(aSourceMapPath, mapping.source)
+            }
+            if (sourceRoot) {
+              mapping.source = util.relative(sourceRoot, mapping.source);
+            }
+            mapping.originalLine = original.line;
+            mapping.originalColumn = original.column;
+            if (original.name !== null && mapping.name !== null) {
+              // Only use the identifier name if it's an identifier
+              // in both SourceMaps
+              mapping.name = original.name;
+            }
+          }
+        }
+
+        var source = mapping.source;
+        if (source && !newSources.has(source)) {
+          newSources.add(source);
+        }
+
+        var name = mapping.name;
+        if (name && !newNames.has(name)) {
+          newNames.add(name);
+        }
+
+      }, this);
+      this._sources = newSources;
+      this._names = newNames;
+
+      // Copy sourcesContents of applied map.
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content) {
+          if (aSourceMapPath) {
+            sourceFile = util.join(aSourceMapPath, sourceFile);
+          }
+          if (sourceRoot) {
+            sourceFile = util.relative(sourceRoot, sourceFile);
+          }
+          this.setSourceContent(sourceFile, content);
+        }
+      }, this);
+    };
+
+  /**
+   * A mapping can have one of the three levels of data:
+   *
+   *   1. Just the generated position.
+   *   2. The Generated position, original position, and original source.
+   *   3. Generated and original position, original source, as well as a name
+   *      token.
+   *
+   * To maintain consistency, we validate that any new mapping being added falls
+   * in to one of these categories.
+   */
+  SourceMapGenerator.prototype._validateMapping =
+    function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+                                                aName) {
+      if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+          && aGenerated.line > 0 && aGenerated.column >= 0
+          && !aOriginal && !aSource && !aName) {
+        // Case 1.
+        return;
+      }
+      else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+               && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+               && aGenerated.line > 0 && aGenerated.column >= 0
+               && aOriginal.line > 0 && aOriginal.column >= 0
+               && aSource) {
+        // Cases 2 and 3.
+        return;
+      }
+      else {
+        throw new Error('Invalid mapping: ' + JSON.stringify({
+          generated: aGenerated,
+          source: aSource,
+          original: aOriginal,
+          name: aName
+        }));
+      }
+    };
+
+  /**
+   * Serialize the accumulated mappings in to the stream of base 64 VLQs
+   * specified by the source map format.
+   */
+  SourceMapGenerator.prototype._serializeMappings =
+    function SourceMapGenerator_serializeMappings() {
+      var previousGeneratedColumn = 0;
+      var previousGeneratedLine = 1;
+      var previousOriginalColumn = 0;
+      var previousOriginalLine = 0;
+      var previousName = 0;
+      var previousSource = 0;
+      var result = '';
+      var mapping;
+
+      // The mappings must be guaranteed to be in sorted order before we start
+      // serializing them or else the generated line numbers (which are defined
+      // via the ';' separators) will be all messed up. Note: it might be more
+      // performant to maintain the sorting as we insert them, rather than as we
+      // serialize them, but the big O is the same either way.
+      this._mappings.sort(util.compareByGeneratedPositions);
+
+      for (var i = 0, len = this._mappings.length; i < len; i++) {
+        mapping = this._mappings[i];
+
+        if (mapping.generatedLine !== previousGeneratedLine) {
+          previousGeneratedColumn = 0;
+          while (mapping.generatedLine !== previousGeneratedLine) {
+            result += ';';
+            previousGeneratedLine++;
+          }
+        }
+        else {
+          if (i > 0) {
+            if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
+              continue;
+            }
+            result += ',';
+          }
+        }
+
+        result += base64VLQ.encode(mapping.generatedColumn
+                                   - previousGeneratedColumn);
+        previousGeneratedColumn = mapping.generatedColumn;
+
+        if (mapping.source) {
+          result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+                                     - previousSource);
+          previousSource = this._sources.indexOf(mapping.source);
+
+          // lines are stored 0-based in SourceMap spec version 3
+          result += base64VLQ.encode(mapping.originalLine - 1
+                                     - previousOriginalLine);
+          previousOriginalLine = mapping.originalLine - 1;
+
+          result += base64VLQ.encode(mapping.originalColumn
+                                     - previousOriginalColumn);
+          previousOriginalColumn = mapping.originalColumn;
+
+          if (mapping.name) {
+            result += base64VLQ.encode(this._names.indexOf(mapping.name)
+                                       - previousName);
+            previousName = this._names.indexOf(mapping.name);
+          }
+        }
+      }
+
+      return result;
+    };
+
+  SourceMapGenerator.prototype._generateSourcesContent =
+    function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
+      return aSources.map(function (source) {
+        if (!this._sourcesContents) {
+          return null;
+        }
+        if (aSourceRoot) {
+          source = util.relative(aSourceRoot, source);
+        }
+        var key = util.toSetString(source);
+        return Object.prototype.hasOwnProperty.call(this._sourcesContents,
+                                                    key)
+          ? this._sourcesContents[key]
+          : null;
+      }, this);
+    };
+
+  /**
+   * Externalize the source map.
+   */
+  SourceMapGenerator.prototype.toJSON =
+    function SourceMapGenerator_toJSON() {
+      var map = {
+        version: this._version,
+        file: this._file,
+        sources: this._sources.toArray(),
+        names: this._names.toArray(),
+        mappings: this._serializeMappings()
+      };
+      if (this._sourceRoot) {
+        map.sourceRoot = this._sourceRoot;
+      }
+      if (this._sourcesContents) {
+        map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
+      }
+
+      return map;
+    };
+
+  /**
+   * Render the source map being generated to a string.
+   */
+  SourceMapGenerator.prototype.toString =
+    function SourceMapGenerator_toString() {
+      return JSON.stringify(this);
+    };
+
+  exports.SourceMapGenerator = SourceMapGenerator;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-node.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-node.js
new file mode 100644
index 0000000..66a2ebc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/source-node.js
@@ -0,0 +1,400 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
+  var util = require('./util');
+
+  // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
+  // operating systems these days (capturing the result).
+  var REGEX_NEWLINE = /(\r?\n)/g;
+
+  // Matches a Windows-style newline, or any character.
+  var REGEX_CHARACTER = /\r\n|[\s\S]/g;
+
+  /**
+   * SourceNodes provide a way to abstract over interpolating/concatenating
+   * snippets of generated JavaScript source code while maintaining the line and
+   * column information associated with the original source code.
+   *
+   * @param aLine The original line number.
+   * @param aColumn The original column number.
+   * @param aSource The original source's filename.
+   * @param aChunks Optional. An array of strings which are snippets of
+   *        generated JS, or other SourceNodes.
+   * @param aName The original identifier.
+   */
+  function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
+    this.children = [];
+    this.sourceContents = {};
+    this.line = aLine === undefined ? null : aLine;
+    this.column = aColumn === undefined ? null : aColumn;
+    this.source = aSource === undefined ? null : aSource;
+    this.name = aName === undefined ? null : aName;
+    if (aChunks != null) this.add(aChunks);
+  }
+
+  /**
+   * Creates a SourceNode from generated code and a SourceMapConsumer.
+   *
+   * @param aGeneratedCode The generated code
+   * @param aSourceMapConsumer The SourceMap for the generated code
+   */
+  SourceNode.fromStringWithSourceMap =
+    function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
+      // The SourceNode we want to fill with the generated code
+      // and the SourceMap
+      var node = new SourceNode();
+
+      // All even indices of this array are one line of the generated code,
+      // while all odd indices are the newlines between two adjacent lines
+      // (since `REGEX_NEWLINE` captures its match).
+      // Processed fragments are removed from this array, by calling `shiftNextLine`.
+      var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+      var shiftNextLine = function() {
+        var lineContents = remainingLines.shift();
+        // The last line of a file might not have a newline.
+        var newLine = remainingLines.shift() || "";
+        return lineContents + newLine;
+      };
+
+      // We need to remember the position of "remainingLines"
+      var lastGeneratedLine = 1, lastGeneratedColumn = 0;
+
+      // The generate SourceNodes we need a code range.
+      // To extract it current and last mapping is used.
+      // Here we store the last mapping.
+      var lastMapping = null;
+
+      aSourceMapConsumer.eachMapping(function (mapping) {
+        if (lastMapping !== null) {
+          // We add the code from "lastMapping" to "mapping":
+          // First check if there is a new line in between.
+          if (lastGeneratedLine < mapping.generatedLine) {
+            var code = "";
+            // Associate first line with "lastMapping"
+            addMappingWithCode(lastMapping, shiftNextLine());
+            lastGeneratedLine++;
+            lastGeneratedColumn = 0;
+            // The remaining code is added without mapping
+          } else {
+            // There is no new line in between.
+            // Associate the code between "lastGeneratedColumn" and
+            // "mapping.generatedColumn" with "lastMapping"
+            var nextLine = remainingLines[0];
+            var code = nextLine.substr(0, mapping.generatedColumn -
+                                          lastGeneratedColumn);
+            remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+                                                lastGeneratedColumn);
+            lastGeneratedColumn = mapping.generatedColumn;
+            addMappingWithCode(lastMapping, code);
+            // No more remaining code, continue
+            lastMapping = mapping;
+            return;
+          }
+        }
+        // We add the generated code until the first mapping
+        // to the SourceNode without any mapping.
+        // Each line is added as separate string.
+        while (lastGeneratedLine < mapping.generatedLine) {
+          node.add(shiftNextLine());
+          lastGeneratedLine++;
+        }
+        if (lastGeneratedColumn < mapping.generatedColumn) {
+          var nextLine = remainingLines[0];
+          node.add(nextLine.substr(0, mapping.generatedColumn));
+          remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+          lastGeneratedColumn = mapping.generatedColumn;
+        }
+        lastMapping = mapping;
+      }, this);
+      // We have processed all mappings.
+      if (remainingLines.length > 0) {
+        if (lastMapping) {
+          // Associate the remaining code in the current line with "lastMapping"
+          addMappingWithCode(lastMapping, shiftNextLine());
+        }
+        // and add the remaining lines without any mapping
+        node.add(remainingLines.join(""));
+      }
+
+      // Copy sourcesContent into SourceNode
+      aSourceMapConsumer.sources.forEach(function (sourceFile) {
+        var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+        if (content) {
+          node.setSourceContent(sourceFile, content);
+        }
+      });
+
+      return node;
+
+      function addMappingWithCode(mapping, code) {
+        if (mapping === null || mapping.source === undefined) {
+          node.add(code);
+        } else {
+          node.add(new SourceNode(mapping.originalLine,
+                                  mapping.originalColumn,
+                                  mapping.source,
+                                  code,
+                                  mapping.name));
+        }
+      }
+    };
+
+  /**
+   * Add a chunk of generated JS to this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.add = function SourceNode_add(aChunk) {
+    if (Array.isArray(aChunk)) {
+      aChunk.forEach(function (chunk) {
+        this.add(chunk);
+      }, this);
+    }
+    else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+      if (aChunk) {
+        this.children.push(aChunk);
+      }
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Add a chunk of generated JS to the beginning of this source node.
+   *
+   * @param aChunk A string snippet of generated JS code, another instance of
+   *        SourceNode, or an array where each member is one of those things.
+   */
+  SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+    if (Array.isArray(aChunk)) {
+      for (var i = aChunk.length-1; i >= 0; i--) {
+        this.prepend(aChunk[i]);
+      }
+    }
+    else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+      this.children.unshift(aChunk);
+    }
+    else {
+      throw new TypeError(
+        "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+      );
+    }
+    return this;
+  };
+
+  /**
+   * Walk over the tree of JS snippets in this node and its children. The
+   * walking function is called once for each snippet of JS and is passed that
+   * snippet and the its original associated source's line/column location.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+    var chunk;
+    for (var i = 0, len = this.children.length; i < len; i++) {
+      chunk = this.children[i];
+      if (chunk instanceof SourceNode) {
+        chunk.walk(aFn);
+      }
+      else {
+        if (chunk !== '') {
+          aFn(chunk, { source: this.source,
+                       line: this.line,
+                       column: this.column,
+                       name: this.name });
+        }
+      }
+    }
+  };
+
+  /**
+   * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+   * each of `this.children`.
+   *
+   * @param aSep The separator.
+   */
+  SourceNode.prototype.join = function SourceNode_join(aSep) {
+    var newChildren;
+    var i;
+    var len = this.children.length;
+    if (len > 0) {
+      newChildren = [];
+      for (i = 0; i < len-1; i++) {
+        newChildren.push(this.children[i]);
+        newChildren.push(aSep);
+      }
+      newChildren.push(this.children[i]);
+      this.children = newChildren;
+    }
+    return this;
+  };
+
+  /**
+   * Call String.prototype.replace on the very right-most source snippet. Useful
+   * for trimming whitespace from the end of a source node, etc.
+   *
+   * @param aPattern The pattern to replace.
+   * @param aReplacement The thing to replace the pattern with.
+   */
+  SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+    var lastChild = this.children[this.children.length - 1];
+    if (lastChild instanceof SourceNode) {
+      lastChild.replaceRight(aPattern, aReplacement);
+    }
+    else if (typeof lastChild === 'string') {
+      this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
+    }
+    else {
+      this.children.push(''.replace(aPattern, aReplacement));
+    }
+    return this;
+  };
+
+  /**
+   * Set the source content for a source file. This will be added to the SourceMapGenerator
+   * in the sourcesContent field.
+   *
+   * @param aSourceFile The filename of the source file
+   * @param aSourceContent The content of the source file
+   */
+  SourceNode.prototype.setSourceContent =
+    function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
+      this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+    };
+
+  /**
+   * Walk over the tree of SourceNodes. The walking function is called for each
+   * source file content and is passed the filename and source content.
+   *
+   * @param aFn The traversal function.
+   */
+  SourceNode.prototype.walkSourceContents =
+    function SourceNode_walkSourceContents(aFn) {
+      for (var i = 0, len = this.children.length; i < len; i++) {
+        if (this.children[i] instanceof SourceNode) {
+          this.children[i].walkSourceContents(aFn);
+        }
+      }
+
+      var sources = Object.keys(this.sourceContents);
+      for (var i = 0, len = sources.length; i < len; i++) {
+        aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
+      }
+    };
+
+  /**
+   * Return the string representation of this source node. Walks over the tree
+   * and concatenates all the various snippets together to one string.
+   */
+  SourceNode.prototype.toString = function SourceNode_toString() {
+    var str = "";
+    this.walk(function (chunk) {
+      str += chunk;
+    });
+    return str;
+  };
+
+  /**
+   * Returns the string representation of this source node along with a source
+   * map.
+   */
+  SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+    var generated = {
+      code: "",
+      line: 1,
+      column: 0
+    };
+    var map = new SourceMapGenerator(aArgs);
+    var sourceMappingActive = false;
+    var lastOriginalSource = null;
+    var lastOriginalLine = null;
+    var lastOriginalColumn = null;
+    var lastOriginalName = null;
+    this.walk(function (chunk, original) {
+      generated.code += chunk;
+      if (original.source !== null
+          && original.line !== null
+          && original.column !== null) {
+        if(lastOriginalSource !== original.source
+           || lastOriginalLine !== original.line
+           || lastOriginalColumn !== original.column
+           || lastOriginalName !== original.name) {
+          map.addMapping({
+            source: original.source,
+            original: {
+              line: original.line,
+              column: original.column
+            },
+            generated: {
+              line: generated.line,
+              column: generated.column
+            },
+            name: original.name
+          });
+        }
+        lastOriginalSource = original.source;
+        lastOriginalLine = original.line;
+        lastOriginalColumn = original.column;
+        lastOriginalName = original.name;
+        sourceMappingActive = true;
+      } else if (sourceMappingActive) {
+        map.addMapping({
+          generated: {
+            line: generated.line,
+            column: generated.column
+          }
+        });
+        lastOriginalSource = null;
+        sourceMappingActive = false;
+      }
+      chunk.match(REGEX_CHARACTER).forEach(function (ch, idx, array) {
+        if (REGEX_NEWLINE.test(ch)) {
+          generated.line++;
+          generated.column = 0;
+          // Mappings end at eol
+          if (idx + 1 === array.length) {
+            lastOriginalSource = null;
+            sourceMappingActive = false;
+          } else if (sourceMappingActive) {
+            map.addMapping({
+              source: original.source,
+              original: {
+                line: original.line,
+                column: original.column
+              },
+              generated: {
+                line: generated.line,
+                column: generated.column
+              },
+              name: original.name
+            });
+          }
+        } else {
+          generated.column += ch.length;
+        }
+      });
+    });
+    this.walkSourceContents(function (sourceFile, sourceContent) {
+      map.setSourceContent(sourceFile, sourceContent);
+    });
+
+    return { code: generated.code, map: map };
+  };
+
+  exports.SourceNode = SourceNode;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/util.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/util.js
new file mode 100644
index 0000000..4316445
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/lib/source-map/util.js
@@ -0,0 +1,302 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  /**
+   * This is a helper function for getting values from parameter/options
+   * objects.
+   *
+   * @param args The object we are extracting values from
+   * @param name The name of the property we are getting.
+   * @param defaultValue An optional value to return if the property is missing
+   * from the object. If this is not specified and the property is missing, an
+   * error will be thrown.
+   */
+  function getArg(aArgs, aName, aDefaultValue) {
+    if (aName in aArgs) {
+      return aArgs[aName];
+    } else if (arguments.length === 3) {
+      return aDefaultValue;
+    } else {
+      throw new Error('"' + aName + '" is a required argument.');
+    }
+  }
+  exports.getArg = getArg;
+
+  var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
+  var dataUrlRegexp = /^data:.+\,.+$/;
+
+  function urlParse(aUrl) {
+    var match = aUrl.match(urlRegexp);
+    if (!match) {
+      return null;
+    }
+    return {
+      scheme: match[1],
+      auth: match[2],
+      host: match[3],
+      port: match[4],
+      path: match[5]
+    };
+  }
+  exports.urlParse = urlParse;
+
+  function urlGenerate(aParsedUrl) {
+    var url = '';
+    if (aParsedUrl.scheme) {
+      url += aParsedUrl.scheme + ':';
+    }
+    url += '//';
+    if (aParsedUrl.auth) {
+      url += aParsedUrl.auth + '@';
+    }
+    if (aParsedUrl.host) {
+      url += aParsedUrl.host;
+    }
+    if (aParsedUrl.port) {
+      url += ":" + aParsedUrl.port
+    }
+    if (aParsedUrl.path) {
+      url += aParsedUrl.path;
+    }
+    return url;
+  }
+  exports.urlGenerate = urlGenerate;
+
+  /**
+   * Normalizes a path, or the path portion of a URL:
+   *
+   * - Replaces consequtive slashes with one slash.
+   * - Removes unnecessary '.' parts.
+   * - Removes unnecessary '<dir>/..' parts.
+   *
+   * Based on code in the Node.js 'path' core module.
+   *
+   * @param aPath The path or url to normalize.
+   */
+  function normalize(aPath) {
+    var path = aPath;
+    var url = urlParse(aPath);
+    if (url) {
+      if (!url.path) {
+        return aPath;
+      }
+      path = url.path;
+    }
+    var isAbsolute = (path.charAt(0) === '/');
+
+    var parts = path.split(/\/+/);
+    for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
+      part = parts[i];
+      if (part === '.') {
+        parts.splice(i, 1);
+      } else if (part === '..') {
+        up++;
+      } else if (up > 0) {
+        if (part === '') {
+          // The first part is blank if the path is absolute. Trying to go
+          // above the root is a no-op. Therefore we can remove all '..' parts
+          // directly after the root.
+          parts.splice(i + 1, up);
+          up = 0;
+        } else {
+          parts.splice(i, 2);
+          up--;
+        }
+      }
+    }
+    path = parts.join('/');
+
+    if (path === '') {
+      path = isAbsolute ? '/' : '.';
+    }
+
+    if (url) {
+      url.path = path;
+      return urlGenerate(url);
+    }
+    return path;
+  }
+  exports.normalize = normalize;
+
+  /**
+   * Joins two paths/URLs.
+   *
+   * @param aRoot The root path or URL.
+   * @param aPath The path or URL to be joined with the root.
+   *
+   * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
+   *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
+   *   first.
+   * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
+   *   is updated with the result and aRoot is returned. Otherwise the result
+   *   is returned.
+   *   - If aPath is absolute, the result is aPath.
+   *   - Otherwise the two paths are joined with a slash.
+   * - Joining for example 'http://' and 'www.example.com' is also supported.
+   */
+  function join(aRoot, aPath) {
+    var aPathUrl = urlParse(aPath);
+    var aRootUrl = urlParse(aRoot);
+    if (aRootUrl) {
+      aRoot = aRootUrl.path || '/';
+    }
+
+    // `join(foo, '//www.example.org')`
+    if (aPathUrl && !aPathUrl.scheme) {
+      if (aRootUrl) {
+        aPathUrl.scheme = aRootUrl.scheme;
+      }
+      return urlGenerate(aPathUrl);
+    }
+
+    if (aPathUrl || aPath.match(dataUrlRegexp)) {
+      return aPath;
+    }
+
+    // `join('http://', 'www.example.com')`
+    if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
+      aRootUrl.host = aPath;
+      return urlGenerate(aRootUrl);
+    }
+
+    var joined = aPath.charAt(0) === '/'
+      ? aPath
+      : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
+
+    if (aRootUrl) {
+      aRootUrl.path = joined;
+      return urlGenerate(aRootUrl);
+    }
+    return joined;
+  }
+  exports.join = join;
+
+  /**
+   * Because behavior goes wacky when you set `__proto__` on objects, we
+   * have to prefix all the strings in our set with an arbitrary character.
+   *
+   * See https://github.com/mozilla/source-map/pull/31 and
+   * https://github.com/mozilla/source-map/issues/30
+   *
+   * @param String aStr
+   */
+  function toSetString(aStr) {
+    return '$' + aStr;
+  }
+  exports.toSetString = toSetString;
+
+  function fromSetString(aStr) {
+    return aStr.substr(1);
+  }
+  exports.fromSetString = fromSetString;
+
+  function relative(aRoot, aPath) {
+    aRoot = aRoot.replace(/\/$/, '');
+
+    var url = urlParse(aRoot);
+    if (aPath.charAt(0) == "/" && url && url.path == "/") {
+      return aPath.slice(1);
+    }
+
+    return aPath.indexOf(aRoot + '/') === 0
+      ? aPath.substr(aRoot.length + 1)
+      : aPath;
+  }
+  exports.relative = relative;
+
+  function strcmp(aStr1, aStr2) {
+    var s1 = aStr1 || "";
+    var s2 = aStr2 || "";
+    return (s1 > s2) - (s1 < s2);
+  }
+
+  /**
+   * Comparator between two mappings where the original positions are compared.
+   *
+   * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+   * mappings with the same original source/line/column, but different generated
+   * line and column the same. Useful when searching for a mapping with a
+   * stubbed out mapping.
+   */
+  function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
+    var cmp;
+
+    cmp = strcmp(mappingA.source, mappingB.source);
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp || onlyCompareOriginal) {
+      return cmp;
+    }
+
+    cmp = strcmp(mappingA.name, mappingB.name);
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    return mappingA.generatedColumn - mappingB.generatedColumn;
+  };
+  exports.compareByOriginalPositions = compareByOriginalPositions;
+
+  /**
+   * Comparator between two mappings where the generated positions are
+   * compared.
+   *
+   * Optionally pass in `true` as `onlyCompareGenerated` to consider two
+   * mappings with the same generated line and column, but different
+   * source/name/original line and column the same. Useful when searching for a
+   * mapping with a stubbed out mapping.
+   */
+  function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
+    var cmp;
+
+    cmp = mappingA.generatedLine - mappingB.generatedLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+    if (cmp || onlyCompareGenerated) {
+      return cmp;
+    }
+
+    cmp = strcmp(mappingA.source, mappingB.source);
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalLine - mappingB.originalLine;
+    if (cmp) {
+      return cmp;
+    }
+
+    cmp = mappingA.originalColumn - mappingB.originalColumn;
+    if (cmp) {
+      return cmp;
+    }
+
+    return strcmp(mappingA.name, mappingB.name);
+  };
+  exports.compareByGeneratedPositions = compareByGeneratedPositions;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/LICENSE
new file mode 100644
index 0000000..af46c6d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/LICENSE
@@ -0,0 +1,58 @@
+amdefine is released under two licenses: new BSD, and MIT. You may pick the
+license that best suits your development needs. The text of both licenses are
+provided below.
+
+
+The "New" BSD License:
+----------------------
+
+Copyright (c) 2011-2015, The Dojo Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  * Neither the name of the Dojo Foundation nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+MIT License
+-----------
+
+Copyright (c) 2011-2015, The Dojo Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/README.md
new file mode 100644
index 0000000..037a6e8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/README.md
@@ -0,0 +1,171 @@
+# amdefine
+
+A module that can be used to implement AMD's define() in Node. This allows you
+to code to the AMD API and have the module work in node programs without
+requiring those other programs to use AMD.
+
+## Usage
+
+**1)** Update your package.json to indicate amdefine as a dependency:
+
+```javascript
+    "dependencies": {
+        "amdefine": ">=0.1.0"
+    }
+```
+
+Then run `npm install` to get amdefine into your project.
+
+**2)** At the top of each module that uses define(), place this code:
+
+```javascript
+if (typeof define !== 'function') { var define = require('amdefine')(module) }
+```
+
+**Only use these snippets** when loading amdefine. If you preserve the basic structure,
+with the braces, it will be stripped out when using the [RequireJS optimizer](#optimizer).
+
+You can add spaces, line breaks and even require amdefine with a local path, but
+keep the rest of the structure to get the stripping behavior.
+
+As you may know, because `if` statements in JavaScript don't have their own scope, the var
+declaration in the above snippet is made whether the `if` expression is truthy or not. If
+RequireJS is loaded then the declaration is superfluous because `define` is already already
+declared in the same scope in RequireJS. Fortunately JavaScript handles multiple `var`
+declarations of the same variable in the same scope gracefully.
+
+If you want to deliver amdefine.js with your code rather than specifying it as a dependency
+with npm, then just download the latest release and refer to it using a relative path:
+
+[Latest Version](https://github.com/jrburke/amdefine/raw/latest/amdefine.js)
+
+### amdefine/intercept
+
+Consider this very experimental.
+
+Instead of pasting the piece of text for the amdefine setup of a `define`
+variable in each module you create or consume, you can use `amdefine/intercept`
+instead. It will automatically insert the above snippet in each .js file loaded
+by Node.
+
+**Warning**: you should only use this if you are creating an application that
+is consuming AMD style defined()'d modules that are distributed via npm and want
+to run that code in Node.
+
+For library code where you are not sure if it will be used by others in Node or
+in the browser, then explicitly depending on amdefine and placing the code
+snippet above is suggested path, instead of using `amdefine/intercept`. The
+intercept module affects all .js files loaded in the Node app, and it is
+inconsiderate to modify global state like that unless you are also controlling
+the top level app.
+
+#### Why distribute AMD-style modules via npm?
+
+npm has a lot of weaknesses for front-end use (installed layout is not great,
+should have better support for the `baseUrl + moduleID + '.js' style of loading,
+single file JS installs), but some people want a JS package manager and are
+willing to live with those constraints. If that is you, but still want to author
+in AMD style modules to get dynamic require([]), better direct source usage and
+powerful loader plugin support in the browser, then this tool can help.
+
+#### amdefine/intercept usage
+
+Just require it in your top level app module (for example index.js, server.js):
+
+```javascript
+require('amdefine/intercept');
+```
+
+The module does not return a value, so no need to assign the result to a local
+variable.
+
+Then just require() code as you normally would with Node's require(). Any .js
+loaded after the intercept require will have the amdefine check injected in
+the .js source as it is loaded. It does not modify the source on disk, just
+prepends some content to the text of the module as it is loaded by Node.
+
+#### How amdefine/intercept works
+
+It overrides the `Module._extensions['.js']` in Node to automatically prepend
+the amdefine snippet above. So, it will affect any .js file loaded by your
+app.
+
+## define() usage
+
+It is best if you use the anonymous forms of define() in your module:
+
+```javascript
+define(function (require) {
+    var dependency = require('dependency');
+});
+```
+
+or
+
+```javascript
+define(['dependency'], function (dependency) {
+
+});
+```
+
+## RequireJS optimizer integration. <a name="optimizer"></name>
+
+Version 1.0.3 of the [RequireJS optimizer](http://requirejs.org/docs/optimization.html)
+will have support for stripping the `if (typeof define !== 'function')` check
+mentioned above, so you can include this snippet for code that runs in the
+browser, but avoid taking the cost of the if() statement once the code is
+optimized for deployment.
+
+## Node 0.4 Support
+
+If you want to support Node 0.4, then add `require` as the second parameter to amdefine:
+
+```javascript
+//Only if you want Node 0.4. If using 0.5 or later, use the above snippet.
+if (typeof define !== 'function') { var define = require('amdefine')(module, require) }
+```
+
+## Limitations
+
+### Synchronous vs Asynchronous
+
+amdefine creates a define() function that is callable by your code. It will
+execute and trace dependencies and call the factory function *synchronously*,
+to keep the behavior in line with Node's synchronous dependency tracing.
+
+The exception: calling AMD's callback-style require() from inside a factory
+function. The require callback is called on process.nextTick():
+
+```javascript
+define(function (require) {
+    require(['a'], function(a) {
+        //'a' is loaded synchronously, but
+        //this callback is called on process.nextTick().
+    });
+});
+```
+
+### Loader Plugins
+
+Loader plugins are supported as long as they call their load() callbacks
+synchronously. So ones that do network requests will not work. However plugins
+like [text](http://requirejs.org/docs/api.html#text) can load text files locally.
+
+The plugin API's `load.fromText()` is **not supported** in amdefine, so this means
+transpiler plugins like the [CoffeeScript loader plugin](https://github.com/jrburke/require-cs)
+will not work. This may be fixable, but it is a bit complex, and I do not have
+enough node-fu to figure it out yet. See the source for amdefine.js if you want
+to get an idea of the issues involved.
+
+## Tests
+
+To run the tests, cd to **tests** and run:
+
+```
+node all.js
+node all-intercept.js
+```
+
+## License
+
+New BSD and MIT. Check the LICENSE file for all the details.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/amdefine.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/amdefine.js
new file mode 100644
index 0000000..0c4a954
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/amdefine.js
@@ -0,0 +1,301 @@
+/** vim: et:ts=4:sw=4:sts=4
+ * @license amdefine 1.0.0 Copyright (c) 2011-2015, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/amdefine for details
+ */
+
+/*jslint node: true */
+/*global module, process */
+'use strict';
+
+/**
+ * Creates a define for node.
+ * @param {Object} module the "module" object that is defined by Node for the
+ * current module.
+ * @param {Function} [requireFn]. Node's require function for the current module.
+ * It only needs to be passed in Node versions before 0.5, when module.require
+ * did not exist.
+ * @returns {Function} a define function that is usable for the current node
+ * module.
+ */
+function amdefine(module, requireFn) {
+    'use strict';
+    var defineCache = {},
+        loaderCache = {},
+        alreadyCalled = false,
+        path = require('path'),
+        makeRequire, stringRequire;
+
+    /**
+     * Trims the . and .. from an array of path segments.
+     * It will keep a leading path segment if a .. will become
+     * the first path segment, to help with module name lookups,
+     * which act like paths, but can be remapped. But the end result,
+     * all paths that use this function should look normalized.
+     * NOTE: this method MODIFIES the input array.
+     * @param {Array} ary the array of path segments.
+     */
+    function trimDots(ary) {
+        var i, part;
+        for (i = 0; ary[i]; i+= 1) {
+            part = ary[i];
+            if (part === '.') {
+                ary.splice(i, 1);
+                i -= 1;
+            } else if (part === '..') {
+                if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+                    //End of the line. Keep at least one non-dot
+                    //path segment at the front so it can be mapped
+                    //correctly to disk. Otherwise, there is likely
+                    //no path mapping for a path starting with '..'.
+                    //This can still fail, but catches the most reasonable
+                    //uses of ..
+                    break;
+                } else if (i > 0) {
+                    ary.splice(i - 1, 2);
+                    i -= 2;
+                }
+            }
+        }
+    }
+
+    function normalize(name, baseName) {
+        var baseParts;
+
+        //Adjust any relative paths.
+        if (name && name.charAt(0) === '.') {
+            //If have a base name, try to normalize against it,
+            //otherwise, assume it is a top-level require that will
+            //be relative to baseUrl in the end.
+            if (baseName) {
+                baseParts = baseName.split('/');
+                baseParts = baseParts.slice(0, baseParts.length - 1);
+                baseParts = baseParts.concat(name.split('/'));
+                trimDots(baseParts);
+                name = baseParts.join('/');
+            }
+        }
+
+        return name;
+    }
+
+    /**
+     * Create the normalize() function passed to a loader plugin's
+     * normalize method.
+     */
+    function makeNormalize(relName) {
+        return function (name) {
+            return normalize(name, relName);
+        };
+    }
+
+    function makeLoad(id) {
+        function load(value) {
+            loaderCache[id] = value;
+        }
+
+        load.fromText = function (id, text) {
+            //This one is difficult because the text can/probably uses
+            //define, and any relative paths and requires should be relative
+            //to that id was it would be found on disk. But this would require
+            //bootstrapping a module/require fairly deeply from node core.
+            //Not sure how best to go about that yet.
+            throw new Error('amdefine does not implement load.fromText');
+        };
+
+        return load;
+    }
+
+    makeRequire = function (systemRequire, exports, module, relId) {
+        function amdRequire(deps, callback) {
+            if (typeof deps === 'string') {
+                //Synchronous, single module require('')
+                return stringRequire(systemRequire, exports, module, deps, relId);
+            } else {
+                //Array of dependencies with a callback.
+
+                //Convert the dependencies to modules.
+                deps = deps.map(function (depName) {
+                    return stringRequire(systemRequire, exports, module, depName, relId);
+                });
+
+                //Wait for next tick to call back the require call.
+                if (callback) {
+                    process.nextTick(function () {
+                        callback.apply(null, deps);
+                    });
+                }
+            }
+        }
+
+        amdRequire.toUrl = function (filePath) {
+            if (filePath.indexOf('.') === 0) {
+                return normalize(filePath, path.dirname(module.filename));
+            } else {
+                return filePath;
+            }
+        };
+
+        return amdRequire;
+    };
+
+    //Favor explicit value, passed in if the module wants to support Node 0.4.
+    requireFn = requireFn || function req() {
+        return module.require.apply(module, arguments);
+    };
+
+    function runFactory(id, deps, factory) {
+        var r, e, m, result;
+
+        if (id) {
+            e = loaderCache[id] = {};
+            m = {
+                id: id,
+                uri: __filename,
+                exports: e
+            };
+            r = makeRequire(requireFn, e, m, id);
+        } else {
+            //Only support one define call per file
+            if (alreadyCalled) {
+                throw new Error('amdefine with no module ID cannot be called more than once per file.');
+            }
+            alreadyCalled = true;
+
+            //Use the real variables from node
+            //Use module.exports for exports, since
+            //the exports in here is amdefine exports.
+            e = module.exports;
+            m = module;
+            r = makeRequire(requireFn, e, m, module.id);
+        }
+
+        //If there are dependencies, they are strings, so need
+        //to convert them to dependency values.
+        if (deps) {
+            deps = deps.map(function (depName) {
+                return r(depName);
+            });
+        }
+
+        //Call the factory with the right dependencies.
+        if (typeof factory === 'function') {
+            result = factory.apply(m.exports, deps);
+        } else {
+            result = factory;
+        }
+
+        if (result !== undefined) {
+            m.exports = result;
+            if (id) {
+                loaderCache[id] = m.exports;
+            }
+        }
+    }
+
+    stringRequire = function (systemRequire, exports, module, id, relId) {
+        //Split the ID by a ! so that
+        var index = id.indexOf('!'),
+            originalId = id,
+            prefix, plugin;
+
+        if (index === -1) {
+            id = normalize(id, relId);
+
+            //Straight module lookup. If it is one of the special dependencies,
+            //deal with it, otherwise, delegate to node.
+            if (id === 'require') {
+                return makeRequire(systemRequire, exports, module, relId);
+            } else if (id === 'exports') {
+                return exports;
+            } else if (id === 'module') {
+                return module;
+            } else if (loaderCache.hasOwnProperty(id)) {
+                return loaderCache[id];
+            } else if (defineCache[id]) {
+                runFactory.apply(null, defineCache[id]);
+                return loaderCache[id];
+            } else {
+                if(systemRequire) {
+                    return systemRequire(originalId);
+                } else {
+                    throw new Error('No module with ID: ' + id);
+                }
+            }
+        } else {
+            //There is a plugin in play.
+            prefix = id.substring(0, index);
+            id = id.substring(index + 1, id.length);
+
+            plugin = stringRequire(systemRequire, exports, module, prefix, relId);
+
+            if (plugin.normalize) {
+                id = plugin.normalize(id, makeNormalize(relId));
+            } else {
+                //Normalize the ID normally.
+                id = normalize(id, relId);
+            }
+
+            if (loaderCache[id]) {
+                return loaderCache[id];
+            } else {
+                plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
+
+                return loaderCache[id];
+            }
+        }
+    };
+
+    //Create a define function specific to the module asking for amdefine.
+    function define(id, deps, factory) {
+        if (Array.isArray(id)) {
+            factory = deps;
+            deps = id;
+            id = undefined;
+        } else if (typeof id !== 'string') {
+            factory = id;
+            id = deps = undefined;
+        }
+
+        if (deps && !Array.isArray(deps)) {
+            factory = deps;
+            deps = undefined;
+        }
+
+        if (!deps) {
+            deps = ['require', 'exports', 'module'];
+        }
+
+        //Set up properties for this module. If an ID, then use
+        //internal cache. If no ID, then use the external variables
+        //for this node module.
+        if (id) {
+            //Put the module in deep freeze until there is a
+            //require call for it.
+            defineCache[id] = [id, deps, factory];
+        } else {
+            runFactory(id, deps, factory);
+        }
+    }
+
+    //define.require, which has access to all the values in the
+    //cache. Useful for AMD modules that all have IDs in the file,
+    //but need to finally export a value to node based on one of those
+    //IDs.
+    define.require = function (id) {
+        if (loaderCache[id]) {
+            return loaderCache[id];
+        }
+
+        if (defineCache[id]) {
+            runFactory.apply(null, defineCache[id]);
+            return loaderCache[id];
+        }
+    };
+
+    define.amd = {};
+
+    return define;
+}
+
+module.exports = amdefine;
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/intercept.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/intercept.js
new file mode 100644
index 0000000..771a983
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/intercept.js
@@ -0,0 +1,36 @@
+/*jshint node: true */
+var inserted,
+    Module = require('module'),
+    fs = require('fs'),
+    existingExtFn = Module._extensions['.js'],
+    amdefineRegExp = /amdefine\.js/;
+
+inserted  = "if (typeof define !== 'function') {var define = require('amdefine')(module)}";
+
+//From the node/lib/module.js source:
+function stripBOM(content) {
+    // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
+    // because the buffer-to-string conversion in `fs.readFileSync()`
+    // translates it to FEFF, the UTF-16 BOM.
+    if (content.charCodeAt(0) === 0xFEFF) {
+        content = content.slice(1);
+    }
+    return content;
+}
+
+//Also adapted from the node/lib/module.js source:
+function intercept(module, filename) {
+    var content = stripBOM(fs.readFileSync(filename, 'utf8'));
+
+    if (!amdefineRegExp.test(module.id)) {
+        content = inserted + content;
+    }
+
+    module._compile(content, filename);
+}
+
+intercept._id = 'amdefine/intercept';
+
+if (!existingExtFn._id || existingExtFn._id !== intercept._id) {
+    Module._extensions['.js'] = intercept;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/package.json
new file mode 100644
index 0000000..897ee44
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/node_modules/amdefine/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "amdefine",
+  "description": "Provide AMD's define() API for declaring modules in the AMD format",
+  "version": "1.0.0",
+  "homepage": "http://github.com/jrburke/amdefine",
+  "author": {
+    "name": "James Burke",
+    "email": "jrburke@gmail.com",
+    "url": "http://github.com/jrburke"
+  },
+  "license": "BSD-3-Clause AND MIT",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jrburke/amdefine.git"
+  },
+  "main": "./amdefine.js",
+  "engines": {
+    "node": ">=0.4.2"
+  },
+  "gitHead": "578bc4a3f7dede33f3f3e10edde0c1607005d761",
+  "bugs": {
+    "url": "https://github.com/jrburke/amdefine/issues"
+  },
+  "_id": "amdefine@1.0.0",
+  "scripts": {},
+  "_shasum": "fd17474700cb5cc9c2b709f0be9d23ce3c198c33",
+  "_from": "amdefine@>=0.0.4",
+  "_npmVersion": "2.12.1",
+  "_nodeVersion": "0.10.36",
+  "_npmUser": {
+    "name": "jrburke",
+    "email": "jrburke@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "jrburke",
+      "email": "jrburke@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "fd17474700cb5cc9c2b709f0be9d23ce3c198c33",
+    "tarball": "http://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/package.json
new file mode 100644
index 0000000..158e14c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/package.json
@@ -0,0 +1,153 @@
+{
+  "name": "source-map",
+  "description": "Generates and consumes source maps",
+  "version": "0.1.34",
+  "homepage": "https://github.com/mozilla/source-map",
+  "author": {
+    "name": "Nick Fitzgerald",
+    "email": "nfitzgerald@mozilla.com"
+  },
+  "contributors": [
+    {
+      "name": "Tobias Koppers",
+      "email": "tobias.koppers@googlemail.com"
+    },
+    {
+      "name": "Duncan Beevers",
+      "email": "duncan@dweebd.com"
+    },
+    {
+      "name": "Stephen Crane",
+      "email": "scrane@mozilla.com"
+    },
+    {
+      "name": "Ryan Seddon",
+      "email": "seddon.ryan@gmail.com"
+    },
+    {
+      "name": "Miles Elam",
+      "email": "miles.elam@deem.com"
+    },
+    {
+      "name": "Mihai Bazon",
+      "email": "mihai.bazon@gmail.com"
+    },
+    {
+      "name": "Michael Ficarra",
+      "email": "github.public.email@michael.ficarra.me"
+    },
+    {
+      "name": "Todd Wolfson",
+      "email": "todd@twolfson.com"
+    },
+    {
+      "name": "Alexander Solovyov",
+      "email": "alexander@solovyov.net"
+    },
+    {
+      "name": "Felix Gnass",
+      "email": "fgnass@gmail.com"
+    },
+    {
+      "name": "Conrad Irwin",
+      "email": "conrad.irwin@gmail.com"
+    },
+    {
+      "name": "usrbincc",
+      "email": "usrbincc@yahoo.com"
+    },
+    {
+      "name": "David Glasser",
+      "email": "glasser@davidglasser.net"
+    },
+    {
+      "name": "Chase Douglas",
+      "email": "chase@newrelic.com"
+    },
+    {
+      "name": "Evan Wallace",
+      "email": "evan.exe@gmail.com"
+    },
+    {
+      "name": "Heather Arthur",
+      "email": "fayearthur@gmail.com"
+    },
+    {
+      "name": "Hugh Kennedy",
+      "email": "hughskennedy@gmail.com"
+    },
+    {
+      "name": "David Glasser",
+      "email": "glasser@davidglasser.net"
+    },
+    {
+      "name": "Simon Lydell",
+      "email": "simon.lydell@gmail.com"
+    },
+    {
+      "name": "Jmeas Smith",
+      "email": "jellyes2@gmail.com"
+    },
+    {
+      "name": "Michael Z Goddard",
+      "email": "mzgoddard@gmail.com"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/mozilla/source-map.git"
+  },
+  "directories": {
+    "lib": "./lib"
+  },
+  "main": "./lib/source-map.js",
+  "engines": {
+    "node": ">=0.8.0"
+  },
+  "licenses": [
+    {
+      "type": "BSD",
+      "url": "http://opensource.org/licenses/BSD-3-Clause"
+    }
+  ],
+  "dependencies": {
+    "amdefine": ">=0.0.4"
+  },
+  "devDependencies": {
+    "dryice": ">=0.4.8"
+  },
+  "scripts": {
+    "test": "node test/run-tests.js",
+    "build": "node Makefile.dryice.js"
+  },
+  "bugs": {
+    "url": "https://github.com/mozilla/source-map/issues"
+  },
+  "_id": "source-map@0.1.34",
+  "_shasum": "a7cfe89aec7b1682c3b198d0acfb47d7d090566b",
+  "_from": "source-map@0.1.34",
+  "_npmVersion": "1.4.9",
+  "_npmUser": {
+    "name": "nickfitzgerald",
+    "email": "fitzgen@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "mozilla-devtools",
+      "email": "mozilla-developer-tools@googlegroups.com"
+    },
+    {
+      "name": "mozilla",
+      "email": "dherman@mozilla.com"
+    },
+    {
+      "name": "nickfitzgerald",
+      "email": "fitzgen@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "a7cfe89aec7b1682c3b198d0acfb47d7d090566b",
+    "tarball": "http://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz"
+  },
+  "_resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/run-tests.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/run-tests.js
new file mode 100644
index 0000000..64a7c3a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/run-tests.js
@@ -0,0 +1,62 @@
+#!/usr/bin/env node
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var util = require('./source-map/util');
+
+function run(tests) {
+  var total = 0;
+  var passed = 0;
+
+  for (var i = 0; i < tests.length; i++) {
+    for (var k in tests[i].testCase) {
+      if (/^test/.test(k)) {
+        total++;
+        try {
+          tests[i].testCase[k](assert, util);
+          passed++;
+        }
+        catch (e) {
+          console.log('FAILED ' + tests[i].name + ': ' + k + '!');
+          console.log(e.stack);
+        }
+      }
+    }
+  }
+
+  console.log('');
+  console.log(passed + ' / ' + total + ' tests passed.');
+  console.log('');
+
+  return total - passed;
+}
+
+function isTestFile(f) {
+  var testToRun = process.argv[2];
+  return testToRun
+    ? path.basename(testToRun) === f
+    : /^test\-.*?\.js/.test(f);
+}
+
+function toModule(f) {
+  return './source-map/' + f.replace(/\.js$/, '');
+}
+
+var requires = fs.readdirSync(path.join(__dirname, 'source-map'))
+  .filter(isTestFile)
+  .map(toModule);
+
+var code = run(requires.map(require).map(function (mod, i) {
+  return {
+    name: requires[i],
+    testCase: mod
+  };
+}));
+
+process.exit(code);
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-api.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-api.js
new file mode 100644
index 0000000..3801233
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-api.js
@@ -0,0 +1,26 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2012 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var sourceMap;
+  try {
+    sourceMap = require('../../lib/source-map');
+  } catch (e) {
+    sourceMap = {};
+    Components.utils.import('resource:///modules/devtools/SourceMap.jsm', sourceMap);
+  }
+
+  exports['test that the api is properly exposed in the top level'] = function (assert, util) {
+    assert.equal(typeof sourceMap.SourceMapGenerator, "function");
+    assert.equal(typeof sourceMap.SourceMapConsumer, "function");
+    assert.equal(typeof sourceMap.SourceNode, "function");
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-array-set.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-array-set.js
new file mode 100644
index 0000000..b5797ed
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-array-set.js
@@ -0,0 +1,104 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var ArraySet = require('../../lib/source-map/array-set').ArraySet;
+
+  function makeTestSet() {
+    var set = new ArraySet();
+    for (var i = 0; i < 100; i++) {
+      set.add(String(i));
+    }
+    return set;
+  }
+
+  exports['test .has() membership'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.ok(set.has(String(i)));
+    }
+  };
+
+  exports['test .indexOf() elements'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.strictEqual(set.indexOf(String(i)), i);
+    }
+  };
+
+  exports['test .at() indexing'] = function (assert, util) {
+    var set = makeTestSet();
+    for (var i = 0; i < 100; i++) {
+      assert.strictEqual(set.at(i), String(i));
+    }
+  };
+
+  exports['test creating from an array'] = function (assert, util) {
+    var set = ArraySet.fromArray(['foo', 'bar', 'baz', 'quux', 'hasOwnProperty']);
+
+    assert.ok(set.has('foo'));
+    assert.ok(set.has('bar'));
+    assert.ok(set.has('baz'));
+    assert.ok(set.has('quux'));
+    assert.ok(set.has('hasOwnProperty'));
+
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.indexOf('bar'), 1);
+    assert.strictEqual(set.indexOf('baz'), 2);
+    assert.strictEqual(set.indexOf('quux'), 3);
+
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.at(1), 'bar');
+    assert.strictEqual(set.at(2), 'baz');
+    assert.strictEqual(set.at(3), 'quux');
+  };
+
+  exports['test that you can add __proto__; see github issue #30'] = function (assert, util) {
+    var set = new ArraySet();
+    set.add('__proto__');
+    assert.ok(set.has('__proto__'));
+    assert.strictEqual(set.at(0), '__proto__');
+    assert.strictEqual(set.indexOf('__proto__'), 0);
+  };
+
+  exports['test .fromArray() with duplicates'] = function (assert, util) {
+    var set = ArraySet.fromArray(['foo', 'foo']);
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 1);
+
+    set = ArraySet.fromArray(['foo', 'foo'], true);
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.at(1), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 2);
+  };
+
+  exports['test .add() with duplicates'] = function (assert, util) {
+    var set = new ArraySet();
+    set.add('foo');
+
+    set.add('foo');
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 1);
+
+    set.add('foo', true);
+    assert.ok(set.has('foo'));
+    assert.strictEqual(set.at(0), 'foo');
+    assert.strictEqual(set.at(1), 'foo');
+    assert.strictEqual(set.indexOf('foo'), 0);
+    assert.strictEqual(set.toArray().length, 2);
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-base64-vlq.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-base64-vlq.js
new file mode 100644
index 0000000..653a874
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-base64-vlq.js
@@ -0,0 +1,24 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64VLQ = require('../../lib/source-map/base64-vlq');
+
+  exports['test normal encoding and decoding'] = function (assert, util) {
+    var result;
+    for (var i = -255; i < 256; i++) {
+      result = base64VLQ.decode(base64VLQ.encode(i));
+      assert.ok(result);
+      assert.equal(result.value, i);
+      assert.equal(result.rest, "");
+    }
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-base64.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-base64.js
new file mode 100644
index 0000000..ff3a244
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-base64.js
@@ -0,0 +1,35 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var base64 = require('../../lib/source-map/base64');
+
+  exports['test out of range encoding'] = function (assert, util) {
+    assert.throws(function () {
+      base64.encode(-1);
+    });
+    assert.throws(function () {
+      base64.encode(64);
+    });
+  };
+
+  exports['test out of range decoding'] = function (assert, util) {
+    assert.throws(function () {
+      base64.decode('=');
+    });
+  };
+
+  exports['test normal encoding and decoding'] = function (assert, util) {
+    for (var i = 0; i < 64; i++) {
+      assert.equal(base64.decode(base64.encode(i)), i);
+    }
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-binary-search.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-binary-search.js
new file mode 100644
index 0000000..ee30683
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-binary-search.js
@@ -0,0 +1,54 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var binarySearch = require('../../lib/source-map/binary-search');
+
+  function numberCompare(a, b) {
+    return a - b;
+  }
+
+  exports['test too high'] = function (assert, util) {
+    var needle = 30;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.doesNotThrow(function () {
+      binarySearch.search(needle, haystack, numberCompare);
+    });
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 20);
+  };
+
+  exports['test too low'] = function (assert, util) {
+    var needle = 1;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.doesNotThrow(function () {
+      binarySearch.search(needle, haystack, numberCompare);
+    });
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), null);
+  };
+
+  exports['test exact search'] = function (assert, util) {
+    var needle = 4;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 4);
+  };
+
+  exports['test fuzzy search'] = function (assert, util) {
+    var needle = 19;
+    var haystack = [2,4,6,8,10,12,14,16,18,20];
+
+    assert.equal(binarySearch.search(needle, haystack, numberCompare), 18);
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-dog-fooding.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-dog-fooding.js
new file mode 100644
index 0000000..26757b2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-dog-fooding.js
@@ -0,0 +1,84 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+
+  exports['test eating our own dog food'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      file: 'testing.js',
+      sourceRoot: '/wu/tang'
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 1, column: 0 },
+      generated: { line: 2, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 2, column: 0 },
+      generated: { line: 3, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 3, column: 0 },
+      generated: { line: 4, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 4, column: 0 },
+      generated: { line: 5, column: 2 }
+    });
+
+    smg.addMapping({
+      source: 'gza.coffee',
+      original: { line: 5, column: 10 },
+      generated: { line: 6, column: 12 }
+    });
+
+    var smc = new SourceMapConsumer(smg.toString());
+
+    // Exact
+    util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 0, null, smc, assert);
+    util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 0, null, smc, assert);
+    util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 0, null, smc, assert);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 0, null, smc, assert);
+    util.assertMapping(6, 12, '/wu/tang/gza.coffee', 5, 10, null, smc, assert);
+
+    // Fuzzy
+
+    // Generated to original
+    util.assertMapping(2, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(2, 9, '/wu/tang/gza.coffee', 1, 0, null, smc, assert, true);
+    util.assertMapping(3, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(3, 9, '/wu/tang/gza.coffee', 2, 0, null, smc, assert, true);
+    util.assertMapping(4, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(4, 9, '/wu/tang/gza.coffee', 3, 0, null, smc, assert, true);
+    util.assertMapping(5, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(5, 9, '/wu/tang/gza.coffee', 4, 0, null, smc, assert, true);
+    util.assertMapping(6, 0, null, null, null, null, smc, assert, true);
+    util.assertMapping(6, 9, null, null, null, null, smc, assert, true);
+    util.assertMapping(6, 13, '/wu/tang/gza.coffee', 5, 10, null, smc, assert, true);
+
+    // Original to generated
+    util.assertMapping(2, 2, '/wu/tang/gza.coffee', 1, 1, null, smc, assert, null, true);
+    util.assertMapping(3, 2, '/wu/tang/gza.coffee', 2, 3, null, smc, assert, null, true);
+    util.assertMapping(4, 2, '/wu/tang/gza.coffee', 3, 6, null, smc, assert, null, true);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 4, 9, null, smc, assert, null, true);
+    util.assertMapping(5, 2, '/wu/tang/gza.coffee', 5, 9, null, smc, assert, null, true);
+    util.assertMapping(6, 12, '/wu/tang/gza.coffee', 6, 19, null, smc, assert, null, true);
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-map-consumer.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-map-consumer.js
new file mode 100644
index 0000000..acd24d5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-map-consumer.js
@@ -0,0 +1,475 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+
+  exports['test that we can instantiate with a string or an object'] = function (assert, util) {
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(util.testMap);
+    });
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(JSON.stringify(util.testMap));
+    });
+  };
+
+  exports['test that the `sources` field has the original sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var sources = map.sources;
+
+    assert.equal(sources[0], '/the/root/one.js');
+    assert.equal(sources[1], '/the/root/two.js');
+    assert.equal(sources.length, 2);
+  };
+
+  exports['test that the source root is reflected in a mapping\'s source field'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var mapping;
+
+    mapping = map.originalPositionFor({
+      line: 2,
+      column: 1
+    });
+    assert.equal(mapping.source, '/the/root/two.js');
+
+    mapping = map.originalPositionFor({
+      line: 1,
+      column: 1
+    });
+    assert.equal(mapping.source, '/the/root/one.js');
+  };
+
+  exports['test mapping tokens back exactly'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+
+    util.assertMapping(1, 1, '/the/root/one.js', 1, 1, null, map, assert);
+    util.assertMapping(1, 5, '/the/root/one.js', 1, 5, null, map, assert);
+    util.assertMapping(1, 9, '/the/root/one.js', 1, 11, null, map, assert);
+    util.assertMapping(1, 18, '/the/root/one.js', 1, 21, 'bar', map, assert);
+    util.assertMapping(1, 21, '/the/root/one.js', 2, 3, null, map, assert);
+    util.assertMapping(1, 28, '/the/root/one.js', 2, 10, 'baz', map, assert);
+    util.assertMapping(1, 32, '/the/root/one.js', 2, 14, 'bar', map, assert);
+
+    util.assertMapping(2, 1, '/the/root/two.js', 1, 1, null, map, assert);
+    util.assertMapping(2, 5, '/the/root/two.js', 1, 5, null, map, assert);
+    util.assertMapping(2, 9, '/the/root/two.js', 1, 11, null, map, assert);
+    util.assertMapping(2, 18, '/the/root/two.js', 1, 21, 'n', map, assert);
+    util.assertMapping(2, 21, '/the/root/two.js', 2, 3, null, map, assert);
+    util.assertMapping(2, 28, '/the/root/two.js', 2, 10, 'n', map, assert);
+  };
+
+  exports['test mapping tokens fuzzy'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+
+    // Finding original positions
+    util.assertMapping(1, 20, '/the/root/one.js', 1, 21, 'bar', map, assert, true);
+    util.assertMapping(1, 30, '/the/root/one.js', 2, 10, 'baz', map, assert, true);
+    util.assertMapping(2, 12, '/the/root/two.js', 1, 11, null, map, assert, true);
+
+    // Finding generated positions
+    util.assertMapping(1, 18, '/the/root/one.js', 1, 22, 'bar', map, assert, null, true);
+    util.assertMapping(1, 28, '/the/root/one.js', 2, 13, 'baz', map, assert, null, true);
+    util.assertMapping(2, 9, '/the/root/two.js', 1, 16, null, map, assert, null, true);
+  };
+
+  exports['test mappings and end of lines'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      file: 'foo.js'
+    });
+    smg.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 1, column: 1 },
+      source: 'bar.js'
+    });
+    smg.addMapping({
+      original: { line: 2, column: 2 },
+      generated: { line: 2, column: 2 },
+      source: 'bar.js'
+    });
+
+    var map = SourceMapConsumer.fromSourceMap(smg);
+
+    // When finding original positions, mappings end at the end of the line.
+    util.assertMapping(2, 1, null, null, null, null, map, assert, true)
+
+    // When finding generated positions, mappings do not end at the end of the line.
+    util.assertMapping(1, 1, 'bar.js', 2, 1, null, map, assert, null, true);
+  };
+
+  exports['test creating source map consumers with )]}\' prefix'] = function (assert, util) {
+    assert.doesNotThrow(function () {
+      var map = new SourceMapConsumer(")]}'" + JSON.stringify(util.testMap));
+    });
+  };
+
+  exports['test eachMapping'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var previousLine = -Infinity;
+    var previousColumn = -Infinity;
+    map.eachMapping(function (mapping) {
+      assert.ok(mapping.generatedLine >= previousLine);
+
+      if (mapping.source) {
+        assert.equal(mapping.source.indexOf(util.testMap.sourceRoot), 0);
+      }
+
+      if (mapping.generatedLine === previousLine) {
+        assert.ok(mapping.generatedColumn >= previousColumn);
+        previousColumn = mapping.generatedColumn;
+      }
+      else {
+        previousLine = mapping.generatedLine;
+        previousColumn = -Infinity;
+      }
+    });
+  };
+
+  exports['test iterating over mappings in a different order'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var previousLine = -Infinity;
+    var previousColumn = -Infinity;
+    var previousSource = "";
+    map.eachMapping(function (mapping) {
+      assert.ok(mapping.source >= previousSource);
+
+      if (mapping.source === previousSource) {
+        assert.ok(mapping.originalLine >= previousLine);
+
+        if (mapping.originalLine === previousLine) {
+          assert.ok(mapping.originalColumn >= previousColumn);
+          previousColumn = mapping.originalColumn;
+        }
+        else {
+          previousLine = mapping.originalLine;
+          previousColumn = -Infinity;
+        }
+      }
+      else {
+        previousSource = mapping.source;
+        previousLine = -Infinity;
+        previousColumn = -Infinity;
+      }
+    }, null, SourceMapConsumer.ORIGINAL_ORDER);
+  };
+
+  exports['test that we can set the context for `this` in eachMapping'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMap);
+    var context = {};
+    map.eachMapping(function () {
+      assert.equal(this, context);
+    }, context);
+  };
+
+  exports['test that the `sourcesContent` field has the original sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMapWithSourcesContent);
+    var sourcesContent = map.sourcesContent;
+
+    assert.equal(sourcesContent[0], ' ONE.foo = function (bar) {\n   return baz(bar);\n };');
+    assert.equal(sourcesContent[1], ' TWO.inc = function (n) {\n   return n + 1;\n };');
+    assert.equal(sourcesContent.length, 2);
+  };
+
+  exports['test that we can get the original sources for the sources'] = function (assert, util) {
+    var map = new SourceMapConsumer(util.testMapWithSourcesContent);
+    var sources = map.sources;
+
+    assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n   return baz(bar);\n };');
+    assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n   return n + 1;\n };');
+    assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n   return baz(bar);\n };');
+    assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n   return n + 1;\n };');
+    assert.throws(function () {
+      map.sourceContentFor("");
+    }, Error);
+    assert.throws(function () {
+      map.sourceContentFor("/the/root/three.js");
+    }, Error);
+    assert.throws(function () {
+      map.sourceContentFor("three.js");
+    }, Error);
+  };
+
+  exports['test sourceRoot + generatedPositionFor'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'foo/bar',
+      file: 'baz.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'bang.coffee'
+    });
+    map.addMapping({
+      original: { line: 5, column: 5 },
+      generated: { line: 6, column: 6 },
+      source: 'bang.coffee'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    // Should handle without sourceRoot.
+    var pos = map.generatedPositionFor({
+      line: 1,
+      column: 1,
+      source: 'bang.coffee'
+    });
+
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+
+    // Should handle with sourceRoot.
+    var pos = map.generatedPositionFor({
+      line: 1,
+      column: 1,
+      source: 'foo/bar/bang.coffee'
+    });
+
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+  };
+
+  exports['test sourceRoot + originalPositionFor'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'foo/bar',
+      file: 'baz.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'bang.coffee'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var pos = map.originalPositionFor({
+      line: 2,
+      column: 2,
+    });
+
+    // Should always have the prepended source root
+    assert.equal(pos.source, 'foo/bar/bang.coffee');
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+  };
+
+  exports['test github issue #56'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'http://',
+      file: 'www.example.com/foo.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'www.example.com/original.js'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var sources = map.sources;
+    assert.equal(sources.length, 1);
+    assert.equal(sources[0], 'http://www.example.com/original.js');
+  };
+
+  exports['test github issue #43'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'http://example.com',
+      file: 'foo.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'http://cdn.example.com/original.js'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var sources = map.sources;
+    assert.equal(sources.length, 1,
+                 'Should only be one source.');
+    assert.equal(sources[0], 'http://cdn.example.com/original.js',
+                 'Should not be joined with the sourceRoot.');
+  };
+
+  exports['test absolute path, but same host sources'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      sourceRoot: 'http://example.com/foo/bar',
+      file: 'foo.js'
+    });
+    map.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: '/original.js'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var sources = map.sources;
+    assert.equal(sources.length, 1,
+                 'Should only be one source.');
+    assert.equal(sources[0], 'http://example.com/original.js',
+                 'Source should be relative the host of the source root.');
+  };
+
+  exports['test github issue #64'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sourceRoot": "http://example.com/",
+      "sources": ["/a"],
+      "names": [],
+      "mappings": "AACA",
+      "sourcesContent": ["foo"]
+    });
+
+    assert.equal(map.sourceContentFor("a"), "foo");
+    assert.equal(map.sourceContentFor("/a"), "foo");
+  };
+
+  exports['test bug 885597'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sourceRoot": "file:///Users/AlGore/Invented/The/Internet/",
+      "sources": ["/a"],
+      "names": [],
+      "mappings": "AACA",
+      "sourcesContent": ["foo"]
+    });
+
+    var s = map.sources[0];
+    assert.equal(map.sourceContentFor(s), "foo");
+  };
+
+  exports['test github issue #72, duplicate sources'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sources": ["source1.js", "source1.js", "source3.js"],
+      "names": [],
+      "mappings": ";EAAC;;IAEE;;MEEE",
+      "sourceRoot": "http://example.com"
+    });
+
+    var pos = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(pos.source, 'http://example.com/source1.js');
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+
+    var pos = map.originalPositionFor({
+      line: 4,
+      column: 4
+    });
+    assert.equal(pos.source, 'http://example.com/source1.js');
+    assert.equal(pos.line, 3);
+    assert.equal(pos.column, 3);
+
+    var pos = map.originalPositionFor({
+      line: 6,
+      column: 6
+    });
+    assert.equal(pos.source, 'http://example.com/source3.js');
+    assert.equal(pos.line, 5);
+    assert.equal(pos.column, 5);
+  };
+
+  exports['test github issue #72, duplicate names'] = function (assert, util) {
+    var map = new SourceMapConsumer({
+      "version": 3,
+      "file": "foo.js",
+      "sources": ["source.js"],
+      "names": ["name1", "name1", "name3"],
+      "mappings": ";EAACA;;IAEEA;;MAEEE",
+      "sourceRoot": "http://example.com"
+    });
+
+    var pos = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(pos.name, 'name1');
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+
+    var pos = map.originalPositionFor({
+      line: 4,
+      column: 4
+    });
+    assert.equal(pos.name, 'name1');
+    assert.equal(pos.line, 3);
+    assert.equal(pos.column, 3);
+
+    var pos = map.originalPositionFor({
+      line: 6,
+      column: 6
+    });
+    assert.equal(pos.name, 'name3');
+    assert.equal(pos.line, 5);
+    assert.equal(pos.column, 5);
+  };
+
+  exports['test SourceMapConsumer.fromSourceMap'] = function (assert, util) {
+    var smg = new SourceMapGenerator({
+      sourceRoot: 'http://example.com/',
+      file: 'foo.js'
+    });
+    smg.addMapping({
+      original: { line: 1, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'bar.js'
+    });
+    smg.addMapping({
+      original: { line: 2, column: 2 },
+      generated: { line: 4, column: 4 },
+      source: 'baz.js',
+      name: 'dirtMcGirt'
+    });
+    smg.setSourceContent('baz.js', 'baz.js content');
+
+    var smc = SourceMapConsumer.fromSourceMap(smg);
+    assert.equal(smc.file, 'foo.js');
+    assert.equal(smc.sourceRoot, 'http://example.com/');
+    assert.equal(smc.sources.length, 2);
+    assert.equal(smc.sources[0], 'http://example.com/bar.js');
+    assert.equal(smc.sources[1], 'http://example.com/baz.js');
+    assert.equal(smc.sourceContentFor('baz.js'), 'baz.js content');
+
+    var pos = smc.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(pos.line, 1);
+    assert.equal(pos.column, 1);
+    assert.equal(pos.source, 'http://example.com/bar.js');
+    assert.equal(pos.name, null);
+
+    pos = smc.generatedPositionFor({
+      line: 1,
+      column: 1,
+      source: 'http://example.com/bar.js'
+    });
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+
+    pos = smc.originalPositionFor({
+      line: 4,
+      column: 4
+    });
+    assert.equal(pos.line, 2);
+    assert.equal(pos.column, 2);
+    assert.equal(pos.source, 'http://example.com/baz.js');
+    assert.equal(pos.name, 'dirtMcGirt');
+
+    pos = smc.generatedPositionFor({
+      line: 2,
+      column: 2,
+      source: 'http://example.com/baz.js'
+    });
+    assert.equal(pos.line, 4);
+    assert.equal(pos.column, 4);
+  };
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-map-generator.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-map-generator.js
new file mode 100644
index 0000000..227140f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-map-generator.js
@@ -0,0 +1,549 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceNode = require('../../lib/source-map/source-node').SourceNode;
+  var util = require('./util');
+
+  exports['test some simple stuff'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'foo.js',
+      sourceRoot: '.'
+    });
+    assert.ok(true);
+
+    var map = new SourceMapGenerator();
+    assert.ok(true);
+  };
+
+  exports['test JSON serialization'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'foo.js',
+      sourceRoot: '.'
+    });
+    assert.equal(map.toString(), JSON.stringify(map));
+  };
+
+  exports['test adding mappings (case 1)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test adding mappings (case 2)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        source: 'bar.js',
+        original: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test adding mappings (case 3)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    assert.doesNotThrow(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        source: 'bar.js',
+        original: { line: 1, column: 1 },
+        name: 'someToken'
+      });
+    });
+  };
+
+  exports['test adding mappings (invalid)'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated-foo.js',
+      sourceRoot: '.'
+    });
+
+    // Not enough info.
+    assert.throws(function () {
+      map.addMapping({});
+    });
+
+    // Original file position, but no source.
+    assert.throws(function () {
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        original: { line: 1, column: 1 }
+      });
+    });
+  };
+
+  exports['test that the correct mappings are being generated'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'min.js',
+      sourceRoot: '/the/root'
+    });
+
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 5 },
+      original: { line: 1, column: 5 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 9 },
+      original: { line: 1, column: 11 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 18 },
+      original: { line: 1, column: 21 },
+      source: 'one.js',
+      name: 'bar'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 21 },
+      original: { line: 2, column: 3 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 28 },
+      original: { line: 2, column: 10 },
+      source: 'one.js',
+      name: 'baz'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 32 },
+      original: { line: 2, column: 14 },
+      source: 'one.js',
+      name: 'bar'
+    });
+
+    map.addMapping({
+      generated: { line: 2, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 5 },
+      original: { line: 1, column: 5 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 9 },
+      original: { line: 1, column: 11 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 18 },
+      original: { line: 1, column: 21 },
+      source: 'two.js',
+      name: 'n'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 21 },
+      original: { line: 2, column: 3 },
+      source: 'two.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 28 },
+      original: { line: 2, column: 10 },
+      source: 'two.js',
+      name: 'n'
+    });
+
+    map = JSON.parse(map.toString());
+
+    util.assertEqualMaps(assert, map, util.testMap);
+  };
+
+  exports['test that source content can be set'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'min.js',
+      sourceRoot: '/the/root'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'one.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 1 },
+      original: { line: 1, column: 1 },
+      source: 'two.js'
+    });
+    map.setSourceContent('one.js', 'one file content');
+
+    map = JSON.parse(map.toString());
+    assert.equal(map.sources[0], 'one.js');
+    assert.equal(map.sources[1], 'two.js');
+    assert.equal(map.sourcesContent[0], 'one file content');
+    assert.equal(map.sourcesContent[1], null);
+  };
+
+  exports['test .fromSourceMap'] = function (assert, util) {
+    var map = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(util.testMap));
+    util.assertEqualMaps(assert, map.toJSON(), util.testMap);
+  };
+
+  exports['test .fromSourceMap with sourcesContent'] = function (assert, util) {
+    var map = SourceMapGenerator.fromSourceMap(
+      new SourceMapConsumer(util.testMapWithSourcesContent));
+    util.assertEqualMaps(assert, map.toJSON(), util.testMapWithSourcesContent);
+  };
+
+  exports['test applySourceMap'] = function (assert, util) {
+    var node = new SourceNode(null, null, null, [
+      new SourceNode(2, 0, 'fileX', 'lineX2\n'),
+      'genA1\n',
+      new SourceNode(2, 0, 'fileY', 'lineY2\n'),
+      'genA2\n',
+      new SourceNode(1, 0, 'fileX', 'lineX1\n'),
+      'genA3\n',
+      new SourceNode(1, 0, 'fileY', 'lineY1\n')
+    ]);
+    var mapStep1 = node.toStringWithSourceMap({
+      file: 'fileA'
+    }).map;
+    mapStep1.setSourceContent('fileX', 'lineX1\nlineX2\n');
+    mapStep1 = mapStep1.toJSON();
+
+    node = new SourceNode(null, null, null, [
+      'gen1\n',
+      new SourceNode(1, 0, 'fileA', 'lineA1\n'),
+      new SourceNode(2, 0, 'fileA', 'lineA2\n'),
+      new SourceNode(3, 0, 'fileA', 'lineA3\n'),
+      new SourceNode(4, 0, 'fileA', 'lineA4\n'),
+      new SourceNode(1, 0, 'fileB', 'lineB1\n'),
+      new SourceNode(2, 0, 'fileB', 'lineB2\n'),
+      'gen2\n'
+    ]);
+    var mapStep2 = node.toStringWithSourceMap({
+      file: 'fileGen'
+    }).map;
+    mapStep2.setSourceContent('fileB', 'lineB1\nlineB2\n');
+    mapStep2 = mapStep2.toJSON();
+
+    node = new SourceNode(null, null, null, [
+      'gen1\n',
+      new SourceNode(2, 0, 'fileX', 'lineA1\n'),
+      new SourceNode(2, 0, 'fileA', 'lineA2\n'),
+      new SourceNode(2, 0, 'fileY', 'lineA3\n'),
+      new SourceNode(4, 0, 'fileA', 'lineA4\n'),
+      new SourceNode(1, 0, 'fileB', 'lineB1\n'),
+      new SourceNode(2, 0, 'fileB', 'lineB2\n'),
+      'gen2\n'
+    ]);
+    var expectedMap = node.toStringWithSourceMap({
+      file: 'fileGen'
+    }).map;
+    expectedMap.setSourceContent('fileX', 'lineX1\nlineX2\n');
+    expectedMap.setSourceContent('fileB', 'lineB1\nlineB2\n');
+    expectedMap = expectedMap.toJSON();
+
+    // apply source map "mapStep1" to "mapStep2"
+    var generator = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(mapStep2));
+    generator.applySourceMap(new SourceMapConsumer(mapStep1));
+    var actualMap = generator.toJSON();
+
+    util.assertEqualMaps(assert, actualMap, expectedMap);
+  };
+
+  exports['test applySourceMap throws when file is missing'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'test.js'
+    });
+    var map2 = new SourceMapGenerator();
+    assert.throws(function() {
+      map.applySourceMap(new SourceMapConsumer(map2.toJSON()));
+    });
+  };
+
+  exports['test the two additional parameters of applySourceMap'] = function (assert, util) {
+    // Assume the following directory structure:
+    //
+    // http://foo.org/
+    //   bar.coffee
+    //   app/
+    //     coffee/
+    //       foo.coffee
+    //     temp/
+    //       bundle.js
+    //     temp_maps/
+    //       bundle.js.map
+    //     public/
+    //       bundle.min.js
+    //       bundle.min.js.map
+    //
+    // http://www.example.com/
+    //   baz.coffee
+
+    var bundleMap = new SourceMapGenerator({
+      file: 'bundle.js'
+    });
+    bundleMap.addMapping({
+      generated: { line: 3, column: 3 },
+      original: { line: 2, column: 2 },
+      source: '../coffee/foo.coffee'
+    });
+    bundleMap.setSourceContent('../coffee/foo.coffee', 'foo coffee');
+    bundleMap.addMapping({
+      generated: { line: 13, column: 13 },
+      original: { line: 12, column: 12 },
+      source: '/bar.coffee'
+    });
+    bundleMap.setSourceContent('/bar.coffee', 'bar coffee');
+    bundleMap.addMapping({
+      generated: { line: 23, column: 23 },
+      original: { line: 22, column: 22 },
+      source: 'http://www.example.com/baz.coffee'
+    });
+    bundleMap.setSourceContent(
+      'http://www.example.com/baz.coffee',
+      'baz coffee'
+    );
+    bundleMap = new SourceMapConsumer(bundleMap.toJSON());
+
+    var minifiedMap = new SourceMapGenerator({
+      file: 'bundle.min.js',
+      sourceRoot: '..'
+    });
+    minifiedMap.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 3, column: 3 },
+      source: 'temp/bundle.js'
+    });
+    minifiedMap.addMapping({
+      generated: { line: 11, column: 11 },
+      original: { line: 13, column: 13 },
+      source: 'temp/bundle.js'
+    });
+    minifiedMap.addMapping({
+      generated: { line: 21, column: 21 },
+      original: { line: 23, column: 23 },
+      source: 'temp/bundle.js'
+    });
+    minifiedMap = new SourceMapConsumer(minifiedMap.toJSON());
+
+    var expectedMap = function (sources) {
+      var map = new SourceMapGenerator({
+        file: 'bundle.min.js',
+        sourceRoot: '..'
+      });
+      map.addMapping({
+        generated: { line: 1, column: 1 },
+        original: { line: 2, column: 2 },
+        source: sources[0]
+      });
+      map.setSourceContent(sources[0], 'foo coffee');
+      map.addMapping({
+        generated: { line: 11, column: 11 },
+        original: { line: 12, column: 12 },
+        source: sources[1]
+      });
+      map.setSourceContent(sources[1], 'bar coffee');
+      map.addMapping({
+        generated: { line: 21, column: 21 },
+        original: { line: 22, column: 22 },
+        source: sources[2]
+      });
+      map.setSourceContent(sources[2], 'baz coffee');
+      return map.toJSON();
+    }
+
+    var actualMap = function (aSourceMapPath) {
+      var map = SourceMapGenerator.fromSourceMap(minifiedMap);
+      // Note that relying on `bundleMap.file` (which is simply 'bundle.js')
+      // instead of supplying the second parameter wouldn't work here.
+      map.applySourceMap(bundleMap, '../temp/bundle.js', aSourceMapPath);
+      return map.toJSON();
+    }
+
+    util.assertEqualMaps(assert, actualMap('../temp_maps'), expectedMap([
+      'coffee/foo.coffee',
+      '/bar.coffee',
+      'http://www.example.com/baz.coffee'
+    ]));
+
+    util.assertEqualMaps(assert, actualMap('/app/temp_maps'), expectedMap([
+      '/app/coffee/foo.coffee',
+      '/bar.coffee',
+      'http://www.example.com/baz.coffee'
+    ]));
+
+    util.assertEqualMaps(assert, actualMap('http://foo.org/app/temp_maps'), expectedMap([
+      'http://foo.org/app/coffee/foo.coffee',
+      'http://foo.org/bar.coffee',
+      'http://www.example.com/baz.coffee'
+    ]));
+  };
+
+  exports['test sorting with duplicate generated mappings'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'test.js'
+    });
+    map.addMapping({
+      generated: { line: 3, column: 0 },
+      original: { line: 2, column: 0 },
+      source: 'a.js'
+    });
+    map.addMapping({
+      generated: { line: 2, column: 0 }
+    });
+    map.addMapping({
+      generated: { line: 2, column: 0 }
+    });
+    map.addMapping({
+      generated: { line: 1, column: 0 },
+      original: { line: 1, column: 0 },
+      source: 'a.js'
+    });
+
+    util.assertEqualMaps(assert, map.toJSON(), {
+      version: 3,
+      file: 'test.js',
+      sources: ['a.js'],
+      names: [],
+      mappings: 'AAAA;A;AACA'
+    });
+  };
+
+  exports['test ignore duplicate mappings.'] = function (assert, util) {
+    var init = { file: 'min.js', sourceRoot: '/the/root' };
+    var map1, map2;
+
+    // null original source location
+    var nullMapping1 = {
+      generated: { line: 1, column: 0 }
+    };
+    var nullMapping2 = {
+      generated: { line: 2, column: 2 }
+    };
+
+    map1 = new SourceMapGenerator(init);
+    map2 = new SourceMapGenerator(init);
+
+    map1.addMapping(nullMapping1);
+    map1.addMapping(nullMapping1);
+
+    map2.addMapping(nullMapping1);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    map1.addMapping(nullMapping2);
+    map1.addMapping(nullMapping1);
+
+    map2.addMapping(nullMapping2);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    // original source location
+    var srcMapping1 = {
+      generated: { line: 1, column: 0 },
+      original: { line: 11, column: 0 },
+      source: 'srcMapping1.js'
+    };
+    var srcMapping2 = {
+      generated: { line: 2, column: 2 },
+      original: { line: 11, column: 0 },
+      source: 'srcMapping2.js'
+    };
+
+    map1 = new SourceMapGenerator(init);
+    map2 = new SourceMapGenerator(init);
+
+    map1.addMapping(srcMapping1);
+    map1.addMapping(srcMapping1);
+
+    map2.addMapping(srcMapping1);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    map1.addMapping(srcMapping2);
+    map1.addMapping(srcMapping1);
+
+    map2.addMapping(srcMapping2);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    // full original source and name information
+    var fullMapping1 = {
+      generated: { line: 1, column: 0 },
+      original: { line: 11, column: 0 },
+      source: 'fullMapping1.js',
+      name: 'fullMapping1'
+    };
+    var fullMapping2 = {
+      generated: { line: 2, column: 2 },
+      original: { line: 11, column: 0 },
+      source: 'fullMapping2.js',
+      name: 'fullMapping2'
+    };
+
+    map1 = new SourceMapGenerator(init);
+    map2 = new SourceMapGenerator(init);
+
+    map1.addMapping(fullMapping1);
+    map1.addMapping(fullMapping1);
+
+    map2.addMapping(fullMapping1);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+
+    map1.addMapping(fullMapping2);
+    map1.addMapping(fullMapping1);
+
+    map2.addMapping(fullMapping2);
+
+    util.assertEqualMaps(assert, map1.toJSON(), map2.toJSON());
+  };
+
+  exports['test github issue #72, check for duplicate names or sources'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'test.js'
+    });
+    map.addMapping({
+      generated: { line: 1, column: 1 },
+      original: { line: 2, column: 2 },
+      source: 'a.js',
+      name: 'foo'
+    });
+    map.addMapping({
+      generated: { line: 3, column: 3 },
+      original: { line: 4, column: 4 },
+      source: 'a.js',
+      name: 'foo'
+    });
+    util.assertEqualMaps(assert, map.toJSON(), {
+      version: 3,
+      file: 'test.js',
+      sources: ['a.js'],
+      names: ['foo'],
+      mappings: 'CACEA;;GAEEA'
+    });
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-node.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-node.js
new file mode 100644
index 0000000..d186521
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-source-node.js
@@ -0,0 +1,487 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var SourceMapGenerator = require('../../lib/source-map/source-map-generator').SourceMapGenerator;
+  var SourceMapConsumer = require('../../lib/source-map/source-map-consumer').SourceMapConsumer;
+  var SourceNode = require('../../lib/source-map/source-node').SourceNode;
+
+  function forEachNewline(fn) {
+    return function (assert, util) {
+      ['\n', '\r\n'].forEach(fn.bind(null, assert, util));
+    }
+  }
+
+  exports['test .add()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null);
+
+    // Adding a string works.
+    node.add('function noop() {}');
+
+    // Adding another source node works.
+    node.add(new SourceNode(null, null, null));
+
+    // Adding an array works.
+    node.add(['function foo() {',
+              new SourceNode(null, null, null,
+                             'return 10;'),
+              '}']);
+
+    // Adding other stuff doesn't.
+    assert.throws(function () {
+      node.add({});
+    });
+    assert.throws(function () {
+      node.add(function () {});
+    });
+  };
+
+  exports['test .prepend()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null);
+
+    // Prepending a string works.
+    node.prepend('function noop() {}');
+    assert.equal(node.children[0], 'function noop() {}');
+    assert.equal(node.children.length, 1);
+
+    // Prepending another source node works.
+    node.prepend(new SourceNode(null, null, null));
+    assert.equal(node.children[0], '');
+    assert.equal(node.children[1], 'function noop() {}');
+    assert.equal(node.children.length, 2);
+
+    // Prepending an array works.
+    node.prepend(['function foo() {',
+              new SourceNode(null, null, null,
+                             'return 10;'),
+              '}']);
+    assert.equal(node.children[0], 'function foo() {');
+    assert.equal(node.children[1], 'return 10;');
+    assert.equal(node.children[2], '}');
+    assert.equal(node.children[3], '');
+    assert.equal(node.children[4], 'function noop() {}');
+    assert.equal(node.children.length, 5);
+
+    // Prepending other stuff doesn't.
+    assert.throws(function () {
+      node.prepend({});
+    });
+    assert.throws(function () {
+      node.prepend(function () {});
+    });
+  };
+
+  exports['test .toString()'] = function (assert, util) {
+    assert.equal((new SourceNode(null, null, null,
+                                 ['function foo() {',
+                                  new SourceNode(null, null, null, 'return 10;'),
+                                  '}'])).toString(),
+                 'function foo() {return 10;}');
+  };
+
+  exports['test .join()'] = function (assert, util) {
+    assert.equal((new SourceNode(null, null, null,
+                                 ['a', 'b', 'c', 'd'])).join(', ').toString(),
+                 'a, b, c, d');
+  };
+
+  exports['test .walk()'] = function (assert, util) {
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', new SourceNode(1, 0, 'a.js', ['someCall()']), ';\n',
+                               '  ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';\n',
+                               '}());']);
+    var expected = [
+      { str: '(function () {\n', source: null,   line: null, column: null },
+      { str: '  ',               source: null,   line: null, column: null },
+      { str: 'someCall()',       source: 'a.js', line: 1,    column: 0    },
+      { str: ';\n',              source: null,   line: null, column: null },
+      { str: '  ',               source: null,   line: null, column: null },
+      { str: 'if (foo) bar()',   source: 'b.js', line: 2,    column: 0    },
+      { str: ';\n',              source: null,   line: null, column: null },
+      { str: '}());',            source: null,   line: null, column: null },
+    ];
+    var i = 0;
+    node.walk(function (chunk, loc) {
+      assert.equal(expected[i].str, chunk);
+      assert.equal(expected[i].source, loc.source);
+      assert.equal(expected[i].line, loc.line);
+      assert.equal(expected[i].column, loc.column);
+      i++;
+    });
+  };
+
+  exports['test .replaceRight'] = function (assert, util) {
+    var node;
+
+    // Not nested
+    node = new SourceNode(null, null, null, 'hello world');
+    node.replaceRight(/world/, 'universe');
+    assert.equal(node.toString(), 'hello universe');
+
+    // Nested
+    node = new SourceNode(null, null, null,
+                          [new SourceNode(null, null, null, 'hey sexy mama, '),
+                           new SourceNode(null, null, null, 'want to kill all humans?')]);
+    node.replaceRight(/kill all humans/, 'watch Futurama');
+    assert.equal(node.toString(), 'hey sexy mama, want to watch Futurama?');
+  };
+
+  exports['test .toStringWithSourceMap()'] = forEachNewline(function (assert, util, nl) {
+    var node = new SourceNode(null, null, null,
+                              ['(function () {' + nl,
+                               '  ',
+                                 new SourceNode(1, 0, 'a.js', 'someCall', 'originalCall'),
+                                 new SourceNode(1, 8, 'a.js', '()'),
+                                 ';' + nl,
+                               '  ', new SourceNode(2, 0, 'b.js', ['if (foo) bar()']), ';' + nl,
+                               '}());']);
+    var result = node.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    assert.equal(result.code, [
+      '(function () {',
+      '  someCall();',
+      '  if (foo) bar();',
+      '}());'
+    ].join(nl));
+
+    var map = result.map;
+    var mapWithoutOptions = node.toStringWithSourceMap().map;
+
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    assert.ok(mapWithoutOptions instanceof SourceMapGenerator, 'mapWithoutOptions instanceof SourceMapGenerator');
+    mapWithoutOptions._file = 'foo.js';
+    util.assertEqualMaps(assert, map.toJSON(), mapWithoutOptions.toJSON());
+
+    map = new SourceMapConsumer(map.toString());
+
+    var actual;
+
+    actual = map.originalPositionFor({
+      line: 1,
+      column: 4
+    });
+    assert.equal(actual.source, null);
+    assert.equal(actual.line, null);
+    assert.equal(actual.column, null);
+
+    actual = map.originalPositionFor({
+      line: 2,
+      column: 2
+    });
+    assert.equal(actual.source, 'a.js');
+    assert.equal(actual.line, 1);
+    assert.equal(actual.column, 0);
+    assert.equal(actual.name, 'originalCall');
+
+    actual = map.originalPositionFor({
+      line: 3,
+      column: 2
+    });
+    assert.equal(actual.source, 'b.js');
+    assert.equal(actual.line, 2);
+    assert.equal(actual.column, 0);
+
+    actual = map.originalPositionFor({
+      line: 3,
+      column: 16
+    });
+    assert.equal(actual.source, null);
+    assert.equal(actual.line, null);
+    assert.equal(actual.column, null);
+
+    actual = map.originalPositionFor({
+      line: 4,
+      column: 2
+    });
+    assert.equal(actual.source, null);
+    assert.equal(actual.line, null);
+    assert.equal(actual.column, null);
+  });
+
+  exports['test .fromStringWithSourceMap()'] = forEachNewline(function (assert, util, nl) {
+    var testCode = util.testGeneratedCode.replace(/\n/g, nl);
+    var node = SourceNode.fromStringWithSourceMap(
+                              testCode,
+                              new SourceMapConsumer(util.testMap));
+
+    var result = node.toStringWithSourceMap({
+      file: 'min.js'
+    });
+    var map = result.map;
+    var code = result.code;
+
+    assert.equal(code, testCode);
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = map.toJSON();
+    assert.equal(map.version, util.testMap.version);
+    assert.equal(map.file, util.testMap.file);
+    assert.equal(map.mappings, util.testMap.mappings);
+  });
+
+  exports['test .fromStringWithSourceMap() empty map'] = forEachNewline(function (assert, util, nl) {
+    var node = SourceNode.fromStringWithSourceMap(
+                              util.testGeneratedCode.replace(/\n/g, nl),
+                              new SourceMapConsumer(util.emptyMap));
+    var result = node.toStringWithSourceMap({
+      file: 'min.js'
+    });
+    var map = result.map;
+    var code = result.code;
+
+    assert.equal(code, util.testGeneratedCode.replace(/\n/g, nl));
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = map.toJSON();
+    assert.equal(map.version, util.emptyMap.version);
+    assert.equal(map.file, util.emptyMap.file);
+    assert.equal(map.mappings.length, util.emptyMap.mappings.length);
+    assert.equal(map.mappings, util.emptyMap.mappings);
+  });
+
+  exports['test .fromStringWithSourceMap() complex version'] = forEachNewline(function (assert, util, nl) {
+    var input = new SourceNode(null, null, null, [
+      "(function() {" + nl,
+        "  var Test = {};" + nl,
+        "  ", new SourceNode(1, 0, "a.js", "Test.A = { value: 1234 };" + nl),
+        "  ", new SourceNode(2, 0, "a.js", "Test.A.x = 'xyz';"), nl,
+        "}());" + nl,
+        "/* Generated Source */"]);
+    input = input.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    var node = SourceNode.fromStringWithSourceMap(
+                              input.code,
+                              new SourceMapConsumer(input.map.toString()));
+
+    var result = node.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+    var map = result.map;
+    var code = result.code;
+
+    assert.equal(code, input.code);
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = map.toJSON();
+    var inputMap = input.map.toJSON();
+    util.assertEqualMaps(assert, map, inputMap);
+  });
+
+  exports['test .toStringWithSourceMap() merging duplicate mappings'] = forEachNewline(function (assert, util, nl) {
+    var input = new SourceNode(null, null, null, [
+      new SourceNode(1, 0, "a.js", "(function"),
+      new SourceNode(1, 0, "a.js", "() {" + nl),
+      "  ",
+      new SourceNode(1, 0, "a.js", "var Test = "),
+      new SourceNode(1, 0, "b.js", "{};" + nl),
+      new SourceNode(2, 0, "b.js", "Test"),
+      new SourceNode(2, 0, "b.js", ".A", "A"),
+      new SourceNode(2, 20, "b.js", " = { value: ", "A"),
+      "1234",
+      new SourceNode(2, 40, "b.js", " };" + nl, "A"),
+      "}());" + nl,
+      "/* Generated Source */"
+    ]);
+    input = input.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    assert.equal(input.code, [
+      "(function() {",
+      "  var Test = {};",
+      "Test.A = { value: 1234 };",
+      "}());",
+      "/* Generated Source */"
+    ].join(nl))
+
+    var correctMap = new SourceMapGenerator({
+      file: 'foo.js'
+    });
+    correctMap.addMapping({
+      generated: { line: 1, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    // Here is no need for a empty mapping,
+    // because mappings ends at eol
+    correctMap.addMapping({
+      generated: { line: 2, column: 2 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 2, column: 13 },
+      source: 'b.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 4 },
+      source: 'b.js',
+      name: 'A',
+      original: { line: 2, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 6 },
+      source: 'b.js',
+      name: 'A',
+      original: { line: 2, column: 20 }
+    });
+    // This empty mapping is required,
+    // because there is a hole in the middle of the line
+    correctMap.addMapping({
+      generated: { line: 3, column: 18 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 22 },
+      source: 'b.js',
+      name: 'A',
+      original: { line: 2, column: 40 }
+    });
+    // Here is no need for a empty mapping,
+    // because mappings ends at eol
+
+    var inputMap = input.map.toJSON();
+    correctMap = correctMap.toJSON();
+    util.assertEqualMaps(assert, inputMap, correctMap);
+  });
+
+  exports['test .toStringWithSourceMap() multi-line SourceNodes'] = forEachNewline(function (assert, util, nl) {
+    var input = new SourceNode(null, null, null, [
+      new SourceNode(1, 0, "a.js", "(function() {" + nl + "var nextLine = 1;" + nl + "anotherLine();" + nl),
+      new SourceNode(2, 2, "b.js", "Test.call(this, 123);" + nl),
+      new SourceNode(2, 2, "b.js", "this['stuff'] = 'v';" + nl),
+      new SourceNode(2, 2, "b.js", "anotherLine();" + nl),
+      "/*" + nl + "Generated" + nl + "Source" + nl + "*/" + nl,
+      new SourceNode(3, 4, "c.js", "anotherLine();" + nl),
+      "/*" + nl + "Generated" + nl + "Source" + nl + "*/"
+    ]);
+    input = input.toStringWithSourceMap({
+      file: 'foo.js'
+    });
+
+    assert.equal(input.code, [
+      "(function() {",
+      "var nextLine = 1;",
+      "anotherLine();",
+      "Test.call(this, 123);",
+      "this['stuff'] = 'v';",
+      "anotherLine();",
+      "/*",
+      "Generated",
+      "Source",
+      "*/",
+      "anotherLine();",
+      "/*",
+      "Generated",
+      "Source",
+      "*/"
+    ].join(nl));
+
+    var correctMap = new SourceMapGenerator({
+      file: 'foo.js'
+    });
+    correctMap.addMapping({
+      generated: { line: 1, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 2, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 3, column: 0 },
+      source: 'a.js',
+      original: { line: 1, column: 0 }
+    });
+    correctMap.addMapping({
+      generated: { line: 4, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 2 }
+    });
+    correctMap.addMapping({
+      generated: { line: 5, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 2 }
+    });
+    correctMap.addMapping({
+      generated: { line: 6, column: 0 },
+      source: 'b.js',
+      original: { line: 2, column: 2 }
+    });
+    correctMap.addMapping({
+      generated: { line: 11, column: 0 },
+      source: 'c.js',
+      original: { line: 3, column: 4 }
+    });
+
+    var inputMap = input.map.toJSON();
+    correctMap = correctMap.toJSON();
+    util.assertEqualMaps(assert, inputMap, correctMap);
+  });
+
+  exports['test .toStringWithSourceMap() with empty string'] = function (assert, util) {
+    var node = new SourceNode(1, 0, 'empty.js', '');
+    var result = node.toStringWithSourceMap();
+    assert.equal(result.code, '');
+  };
+
+  exports['test setSourceContent with toStringWithSourceMap'] = function (assert, util) {
+    var aNode = new SourceNode(1, 1, 'a.js', 'a');
+    aNode.setSourceContent('a.js', 'someContent');
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', aNode,
+                               '  ', new SourceNode(1, 1, 'b.js', 'b'),
+                               '}());']);
+    node.setSourceContent('b.js', 'otherContent');
+    var map = node.toStringWithSourceMap({
+      file: 'foo.js'
+    }).map;
+
+    assert.ok(map instanceof SourceMapGenerator, 'map instanceof SourceMapGenerator');
+    map = new SourceMapConsumer(map.toString());
+
+    assert.equal(map.sources.length, 2);
+    assert.equal(map.sources[0], 'a.js');
+    assert.equal(map.sources[1], 'b.js');
+    assert.equal(map.sourcesContent.length, 2);
+    assert.equal(map.sourcesContent[0], 'someContent');
+    assert.equal(map.sourcesContent[1], 'otherContent');
+  };
+
+  exports['test walkSourceContents'] = function (assert, util) {
+    var aNode = new SourceNode(1, 1, 'a.js', 'a');
+    aNode.setSourceContent('a.js', 'someContent');
+    var node = new SourceNode(null, null, null,
+                              ['(function () {\n',
+                               '  ', aNode,
+                               '  ', new SourceNode(1, 1, 'b.js', 'b'),
+                               '}());']);
+    node.setSourceContent('b.js', 'otherContent');
+    var results = [];
+    node.walkSourceContents(function (sourceFile, sourceContent) {
+      results.push([sourceFile, sourceContent]);
+    });
+    assert.equal(results.length, 2);
+    assert.equal(results[0][0], 'a.js');
+    assert.equal(results[0][1], 'someContent');
+    assert.equal(results[1][0], 'b.js');
+    assert.equal(results[1][1], 'otherContent');
+  };
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-util.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-util.js
new file mode 100644
index 0000000..22e9a9e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/test-util.js
@@ -0,0 +1,127 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var libUtil = require('../../lib/source-map/util');
+
+  exports['test urls'] = function (assert, util) {
+    var assertUrl = function (url) {
+      assert.equal(url, libUtil.urlGenerate(libUtil.urlParse(url)));
+    };
+    assertUrl('http://');
+    assertUrl('http://www.example.com');
+    assertUrl('http://user:pass@www.example.com');
+    assertUrl('http://www.example.com:80');
+    assertUrl('http://www.example.com/');
+    assertUrl('http://www.example.com/foo/bar');
+    assertUrl('http://www.example.com/foo/bar/');
+    assertUrl('http://user:pass@www.example.com:80/foo/bar/');
+
+    assertUrl('//');
+    assertUrl('//www.example.com');
+    assertUrl('file:///www.example.com');
+
+    assert.equal(libUtil.urlParse('a//b'), null);
+  };
+
+  exports['test normalize()'] = function (assert, util) {
+    assert.equal(libUtil.normalize('/..'), '/');
+    assert.equal(libUtil.normalize('/../'), '/');
+    assert.equal(libUtil.normalize('/../../../..'), '/');
+    assert.equal(libUtil.normalize('/../../../../a/b/c'), '/a/b/c');
+    assert.equal(libUtil.normalize('/a/b/c/../../../d/../../e'), '/e');
+
+    assert.equal(libUtil.normalize('..'), '..');
+    assert.equal(libUtil.normalize('../'), '../');
+    assert.equal(libUtil.normalize('../../a/'), '../../a/');
+    assert.equal(libUtil.normalize('a/..'), '.');
+    assert.equal(libUtil.normalize('a/../../..'), '../..');
+
+    assert.equal(libUtil.normalize('/.'), '/');
+    assert.equal(libUtil.normalize('/./'), '/');
+    assert.equal(libUtil.normalize('/./././.'), '/');
+    assert.equal(libUtil.normalize('/././././a/b/c'), '/a/b/c');
+    assert.equal(libUtil.normalize('/a/b/c/./././d/././e'), '/a/b/c/d/e');
+
+    assert.equal(libUtil.normalize('.'), '.');
+    assert.equal(libUtil.normalize('./'), '.');
+    assert.equal(libUtil.normalize('././a'), 'a');
+    assert.equal(libUtil.normalize('a/./'), 'a/');
+    assert.equal(libUtil.normalize('a/././.'), 'a');
+
+    assert.equal(libUtil.normalize('/a/b//c////d/////'), '/a/b/c/d/');
+    assert.equal(libUtil.normalize('///a/b//c////d/////'), '///a/b/c/d/');
+    assert.equal(libUtil.normalize('a/b//c////d'), 'a/b/c/d');
+
+    assert.equal(libUtil.normalize('.///.././../a/b//./..'), '../../a')
+
+    assert.equal(libUtil.normalize('http://www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.normalize('http://www.example.com/'), 'http://www.example.com/');
+    assert.equal(libUtil.normalize('http://www.example.com/./..//a/b/c/.././d//'), 'http://www.example.com/a/b/d/');
+  };
+
+  exports['test join()'] = function (assert, util) {
+    assert.equal(libUtil.join('a', 'b'), 'a/b');
+    assert.equal(libUtil.join('a/', 'b'), 'a/b');
+    assert.equal(libUtil.join('a//', 'b'), 'a/b');
+    assert.equal(libUtil.join('a', 'b/'), 'a/b/');
+    assert.equal(libUtil.join('a', 'b//'), 'a/b/');
+    assert.equal(libUtil.join('a/', '/b'), '/b');
+    assert.equal(libUtil.join('a//', '//b'), '//b');
+
+    assert.equal(libUtil.join('a', '..'), '.');
+    assert.equal(libUtil.join('a', '../b'), 'b');
+    assert.equal(libUtil.join('a/b', '../c'), 'a/c');
+
+    assert.equal(libUtil.join('a', '.'), 'a');
+    assert.equal(libUtil.join('a', './b'), 'a/b');
+    assert.equal(libUtil.join('a/b', './c'), 'a/b/c');
+
+    assert.equal(libUtil.join('a', 'http://www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.join('a', 'data:foo,bar'), 'data:foo,bar');
+
+
+    assert.equal(libUtil.join('http://foo.org/a', 'b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a/', 'b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a//', 'b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a', 'b/'), 'http://foo.org/a/b/');
+    assert.equal(libUtil.join('http://foo.org/a', 'b//'), 'http://foo.org/a/b/');
+    assert.equal(libUtil.join('http://foo.org/a/', '/b'), 'http://foo.org/b');
+    assert.equal(libUtil.join('http://foo.org/a//', '//b'), 'http://b');
+
+    assert.equal(libUtil.join('http://foo.org/a', '..'), 'http://foo.org/');
+    assert.equal(libUtil.join('http://foo.org/a', '../b'), 'http://foo.org/b');
+    assert.equal(libUtil.join('http://foo.org/a/b', '../c'), 'http://foo.org/a/c');
+
+    assert.equal(libUtil.join('http://foo.org/a', '.'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org/a', './b'), 'http://foo.org/a/b');
+    assert.equal(libUtil.join('http://foo.org/a/b', './c'), 'http://foo.org/a/b/c');
+
+    assert.equal(libUtil.join('http://foo.org/a', 'http://www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.join('http://foo.org/a', 'data:foo,bar'), 'data:foo,bar');
+
+
+    assert.equal(libUtil.join('http://foo.org', 'a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org/', 'a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org//', 'a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org', '/a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org/', '/a'), 'http://foo.org/a');
+    assert.equal(libUtil.join('http://foo.org//', '/a'), 'http://foo.org/a');
+
+
+    assert.equal(libUtil.join('http://', 'www.example.com'), 'http://www.example.com');
+    assert.equal(libUtil.join('file:///', 'www.example.com'), 'file:///www.example.com');
+    assert.equal(libUtil.join('http://', 'ftp://example.com'), 'ftp://example.com');
+
+    assert.equal(libUtil.join('http://www.example.com', '//foo.org/bar'), 'http://foo.org/bar');
+    assert.equal(libUtil.join('//www.example.com', '//foo.org/bar'), '//foo.org/bar');
+  };
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/util.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/util.js
new file mode 100644
index 0000000..288046b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/source-map/test/source-map/util.js
@@ -0,0 +1,161 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+    var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+  var util = require('../../lib/source-map/util');
+
+  // This is a test mapping which maps functions from two different files
+  // (one.js and two.js) to a minified generated source.
+  //
+  // Here is one.js:
+  //
+  //   ONE.foo = function (bar) {
+  //     return baz(bar);
+  //   };
+  //
+  // Here is two.js:
+  //
+  //   TWO.inc = function (n) {
+  //     return n + 1;
+  //   };
+  //
+  // And here is the generated code (min.js):
+  //
+  //   ONE.foo=function(a){return baz(a);};
+  //   TWO.inc=function(a){return a+1;};
+  exports.testGeneratedCode = " ONE.foo=function(a){return baz(a);};\n"+
+                              " TWO.inc=function(a){return a+1;};";
+  exports.testMap = {
+    version: 3,
+    file: 'min.js',
+    names: ['bar', 'baz', 'n'],
+    sources: ['one.js', 'two.js'],
+    sourceRoot: '/the/root',
+    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+  };
+  exports.testMapWithSourcesContent = {
+    version: 3,
+    file: 'min.js',
+    names: ['bar', 'baz', 'n'],
+    sources: ['one.js', 'two.js'],
+    sourcesContent: [
+      ' ONE.foo = function (bar) {\n' +
+      '   return baz(bar);\n' +
+      ' };',
+      ' TWO.inc = function (n) {\n' +
+      '   return n + 1;\n' +
+      ' };'
+    ],
+    sourceRoot: '/the/root',
+    mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
+  };
+  exports.emptyMap = {
+    version: 3,
+    file: 'min.js',
+    names: [],
+    sources: [],
+    mappings: ''
+  };
+
+
+  function assertMapping(generatedLine, generatedColumn, originalSource,
+                         originalLine, originalColumn, name, map, assert,
+                         dontTestGenerated, dontTestOriginal) {
+    if (!dontTestOriginal) {
+      var origMapping = map.originalPositionFor({
+        line: generatedLine,
+        column: generatedColumn
+      });
+      assert.equal(origMapping.name, name,
+                   'Incorrect name, expected ' + JSON.stringify(name)
+                   + ', got ' + JSON.stringify(origMapping.name));
+      assert.equal(origMapping.line, originalLine,
+                   'Incorrect line, expected ' + JSON.stringify(originalLine)
+                   + ', got ' + JSON.stringify(origMapping.line));
+      assert.equal(origMapping.column, originalColumn,
+                   'Incorrect column, expected ' + JSON.stringify(originalColumn)
+                   + ', got ' + JSON.stringify(origMapping.column));
+
+      var expectedSource;
+
+      if (originalSource && map.sourceRoot && originalSource.indexOf(map.sourceRoot) === 0) {
+        expectedSource = originalSource;
+      } else if (originalSource) {
+        expectedSource = map.sourceRoot
+          ? util.join(map.sourceRoot, originalSource)
+          : originalSource;
+      } else {
+        expectedSource = null;
+      }
+
+      assert.equal(origMapping.source, expectedSource,
+                   'Incorrect source, expected ' + JSON.stringify(expectedSource)
+                   + ', got ' + JSON.stringify(origMapping.source));
+    }
+
+    if (!dontTestGenerated) {
+      var genMapping = map.generatedPositionFor({
+        source: originalSource,
+        line: originalLine,
+        column: originalColumn
+      });
+      assert.equal(genMapping.line, generatedLine,
+                   'Incorrect line, expected ' + JSON.stringify(generatedLine)
+                   + ', got ' + JSON.stringify(genMapping.line));
+      assert.equal(genMapping.column, generatedColumn,
+                   'Incorrect column, expected ' + JSON.stringify(generatedColumn)
+                   + ', got ' + JSON.stringify(genMapping.column));
+    }
+  }
+  exports.assertMapping = assertMapping;
+
+  function assertEqualMaps(assert, actualMap, expectedMap) {
+    assert.equal(actualMap.version, expectedMap.version, "version mismatch");
+    assert.equal(actualMap.file, expectedMap.file, "file mismatch");
+    assert.equal(actualMap.names.length,
+                 expectedMap.names.length,
+                 "names length mismatch: " +
+                   actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
+    for (var i = 0; i < actualMap.names.length; i++) {
+      assert.equal(actualMap.names[i],
+                   expectedMap.names[i],
+                   "names[" + i + "] mismatch: " +
+                     actualMap.names.join(", ") + " != " + expectedMap.names.join(", "));
+    }
+    assert.equal(actualMap.sources.length,
+                 expectedMap.sources.length,
+                 "sources length mismatch: " +
+                   actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
+    for (var i = 0; i < actualMap.sources.length; i++) {
+      assert.equal(actualMap.sources[i],
+                   expectedMap.sources[i],
+                   "sources[" + i + "] length mismatch: " +
+                   actualMap.sources.join(", ") + " != " + expectedMap.sources.join(", "));
+    }
+    assert.equal(actualMap.sourceRoot,
+                 expectedMap.sourceRoot,
+                 "sourceRoot mismatch: " +
+                   actualMap.sourceRoot + " != " + expectedMap.sourceRoot);
+    assert.equal(actualMap.mappings, expectedMap.mappings,
+                 "mappings mismatch:\nActual:   " + actualMap.mappings + "\nExpected: " + expectedMap.mappings);
+    if (actualMap.sourcesContent) {
+      assert.equal(actualMap.sourcesContent.length,
+                   expectedMap.sourcesContent.length,
+                   "sourcesContent length mismatch");
+      for (var i = 0; i < actualMap.sourcesContent.length; i++) {
+        assert.equal(actualMap.sourcesContent[i],
+                     expectedMap.sourcesContent[i],
+                     "sourcesContent[" + i + "] mismatch");
+      }
+    }
+  }
+  exports.assertEqualMaps = assertEqualMaps;
+
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/.npmignore
new file mode 100644
index 0000000..3e56974
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/.npmignore
@@ -0,0 +1,14 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
+/test/output.js
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/.travis.yml
new file mode 100644
index 0000000..87f8cd9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/LICENSE
new file mode 100644
index 0000000..35cc606
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/README.md
new file mode 100644
index 0000000..c5dde96
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/README.md
@@ -0,0 +1,15 @@
+# uglify-to-browserify
+
+A transform to make UglifyJS work in browserify.
+
+[![Build Status](https://travis-ci.org/ForbesLindesay/uglify-to-browserify.png?branch=master)](https://travis-ci.org/ForbesLindesay/uglify-to-browserify)
+[![Dependency Status](https://gemnasium.com/ForbesLindesay/uglify-to-browserify.png)](https://gemnasium.com/ForbesLindesay/uglify-to-browserify)
+[![NPM version](https://badge.fury.io/js/uglify-to-browserify.png)](http://badge.fury.io/js/uglify-to-browserify)
+
+## Installation
+
+    npm install uglify-to-browserify
+
+## License
+
+  MIT
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/index.js
new file mode 100644
index 0000000..c1741b2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/index.js
@@ -0,0 +1,49 @@
+'use strict'
+
+var fs = require('fs')
+var PassThrough = require('stream').PassThrough
+var Transform = require('stream').Transform
+
+if (typeof Transform === 'undefined') {
+  throw new Error('UglifyJS only supports browserify when using node >= 0.10.x')
+}
+
+var cache = {}
+module.exports = transform
+function transform(file) {
+  if (!/tools\/node\.js$/.test(file.replace(/\\/g,'/'))) return new PassThrough();
+  if (cache[file]) return makeStream(cache[file])
+  var uglify = require(file)
+  var src = 'var sys = require("util");\nvar MOZ_SourceMap = require("source-map");\nvar UglifyJS = exports;\n' + uglify.FILES.map(function (path) { return fs.readFileSync(path, 'utf8') }).join('\n')
+
+  var ast = uglify.parse(src)
+  ast.figure_out_scope()
+
+  var variables = ast.variables
+    .map(function (node, name) {
+      return name
+    })
+
+  src += '\n\n' + variables.map(function (v) { return 'exports.' + v + ' = ' + v + ';' }).join('\n') + '\n\n'
+
+  src += 'exports.AST_Node.warn_function = function (txt) { if (typeof console != "undefined" && typeof console.warn === "function") console.warn(txt) }\n\n'
+
+  src += 'exports.minify = ' + uglify.minify.toString() + ';\n\n'
+  src += 'exports.describe_ast = ' + uglify.describe_ast.toString() + ';'
+
+  // TODO: remove once https://github.com/substack/node-browserify/issues/631 is resolved
+  src = src.replace(/"for"/g, '"fo" + "r"')
+
+  cache[file] = src
+  return makeStream(src);
+}
+
+function makeStream(src) {
+  var res = new Transform();
+  res._transform = function (chunk, encoding, callback) { callback() }
+  res._flush = function (callback) {
+    res.push(src)
+    callback()
+  }
+  return res;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/package.json
new file mode 100644
index 0000000..2728afa
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "uglify-to-browserify",
+  "version": "1.0.2",
+  "description": "A transform to make UglifyJS work in browserify.",
+  "keywords": [],
+  "dependencies": {},
+  "devDependencies": {
+    "uglify-js": "~2.4.0",
+    "source-map": "~0.1.27"
+  },
+  "scripts": {
+    "test": "node test/index.js"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/ForbesLindesay/uglify-to-browserify.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/ForbesLindesay/uglify-to-browserify/issues"
+  },
+  "homepage": "https://github.com/ForbesLindesay/uglify-to-browserify",
+  "_id": "uglify-to-browserify@1.0.2",
+  "dist": {
+    "shasum": "6e0924d6bda6b5afe349e39a6d632850a0f882b7",
+    "tarball": "http://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz"
+  },
+  "_from": "uglify-to-browserify@>=1.0.0 <1.1.0",
+  "_npmVersion": "1.3.14",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindeay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "directories": {},
+  "_shasum": "6e0924d6bda6b5afe349e39a6d632850a0f882b7",
+  "_resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/test/index.js
new file mode 100644
index 0000000..6befb6b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/uglify-to-browserify/test/index.js
@@ -0,0 +1,22 @@
+var fs = require('fs')
+var br = require('../')
+var test = fs.readFileSync(require.resolve('uglify-js/test/run-tests.js'), 'utf8')
+  .replace(/^#.*\n/, '')
+
+var transform = br(require.resolve('uglify-js'))
+transform.pipe(fs.createWriteStream(__dirname + '/output.js'))
+  .on('close', function () {
+    Function('module,require', test)({
+      filename: require.resolve('uglify-js/test/run-tests.js')
+    },
+      function (name) {
+        if (name === '../tools/node') {
+          return require('./output.js')
+        } else if (/^[a-z]+$/.test(name)) {
+          return require(name)
+        } else {
+          throw new Error('I didn\'t expect you to require ' + name)
+        }
+      })
+  })
+transform.end(fs.readFileSync(require.resolve('uglify-js'), 'utf8'))
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/LICENSE
new file mode 100644
index 0000000..432d1ae
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/LICENSE
@@ -0,0 +1,21 @@
+Copyright 2010 James Halliday (mail@substack.net)
+
+This project is free software released under the MIT/X11 license:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/README.md
new file mode 100644
index 0000000..654914f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/README.md
@@ -0,0 +1,901 @@
+yargs
+========
+
+Yargs be a node.js library fer hearties tryin' ter parse optstrings.
+
+With yargs, ye be havin' a map that leads straight to yer treasure! Treasure of course, being a simple option hash.
+
+[![Build Status](https://travis-ci.org/bcoe/yargs.png)](https://travis-ci.org/bcoe/yargs)
+[![Dependency Status](https://gemnasium.com/bcoe/yargs.png)](https://gemnasium.com/bcoe/yargs)
+[![Coverage Status](https://coveralls.io/repos/bcoe/yargs/badge.svg?branch=)](https://coveralls.io/r/bcoe/yargs?branch=)
+[![NPM version](https://img.shields.io/npm/v/yargs.svg)](https://www.npmjs.com/package/yargs)
+
+> Yargs is the official successor to optimist. Please feel free to submit issues and pull requests. If you'd like to contribute and don't know where to start, have a look at [the issue list](https://github.com/bcoe/yargs/issues) :)
+
+examples
+========
+
+With yargs, the options be just a hash!
+-------------------------------------------------------------------
+
+xup.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs').argv;
+
+if (argv.rif - 5 * argv.xup > 7.138) {
+    console.log('Plunder more riffiwobbles!');
+}
+else {
+    console.log('Drop the xupptumblers!');
+}
+````
+
+***
+
+    $ ./xup.js --rif=55 --xup=9.52
+    Plunder more riffiwobbles!
+
+    $ ./xup.js --rif 12 --xup 8.1
+    Drop the xupptumblers!
+
+![Joe was one optimistic pirate.](http://i.imgur.com/4WFGVJ9.png)
+
+But don't walk the plank just yet! There be more! You can do short options:
+-------------------------------------------------
+
+short.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs').argv;
+console.log('(%d,%d)', argv.x, argv.y);
+````
+
+***
+
+    $ ./short.js -x 10 -y 21
+    (10,21)
+
+And booleans, both long, short, and even grouped:
+----------------------------------
+
+bool.js:
+
+````javascript
+#!/usr/bin/env node
+var util = require('util');
+var argv = require('yargs').argv;
+
+if (argv.s) {
+    util.print(argv.fr ? 'Le perroquet dit: ' : 'The parrot says: ');
+}
+console.log(
+    (argv.fr ? 'couac' : 'squawk') + (argv.p ? '!' : '')
+);
+````
+
+***
+
+    $ ./bool.js -s
+    The parrot says: squawk
+
+    $ ./bool.js -sp
+    The parrot says: squawk!
+
+    $ ./bool.js -sp --fr
+    Le perroquet dit: couac!
+
+And non-hyphenated options too! Just use `argv._`!
+-------------------------------------------------
+
+nonopt.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs').argv;
+console.log('(%d,%d)', argv.x, argv.y);
+console.log(argv._);
+````
+
+***
+
+    $ ./nonopt.js -x 6.82 -y 3.35 rum
+    (6.82,3.35)
+    [ 'rum' ]
+
+    $ ./nonopt.js "me hearties" -x 0.54 yo -y 1.12 ho
+    (0.54,1.12)
+    [ 'me hearties', 'yo', 'ho' ]
+
+Yargs even counts your booleans!
+----------------------------------------------------------------------
+
+count.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .count('verbose')
+    .alias('v', 'verbose')
+    .argv;
+
+VERBOSE_LEVEL = argv.verbose;
+
+function WARN()  { VERBOSE_LEVEL >= 0 && console.log.apply(console, arguments); }
+function INFO()  { VERBOSE_LEVEL >= 1 && console.log.apply(console, arguments); }
+function DEBUG() { VERBOSE_LEVEL >= 2 && console.log.apply(console, arguments); }
+
+WARN("Showing only important stuff");
+INFO("Showing semi-mportant stuff too");
+DEBUG("Extra chatty mode");
+````
+
+***
+    $ node count.js
+    Showing only important stuff
+
+    $ node count.js -v
+    Showing only important stuff
+    Showing semi-important stuff too
+
+    $ node count.js -vv
+    Showing only important stuff
+    Showing semi-important stuff too
+    Extra chatty mode
+
+    $ node count.js -v --verbose
+    Showing only important stuff
+    Showing semi-important stuff too
+    Extra chatty mode
+
+Tell users how to use yer options and make demands.
+-------------------------------------------------
+
+divide.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .usage('Usage: $0 -x [num] -y [num]')
+    .demand(['x','y'])
+    .argv;
+
+console.log(argv.x / argv.y);
+````
+
+***
+
+    $ ./divide.js -x 55 -y 11
+    5
+
+    $ node ./divide.js -x 4.91 -z 2.51
+    Usage: node ./divide.js -x [num] -y [num]
+
+    Options:
+      -x  [required]
+      -y  [required]
+
+    Missing required arguments: y
+
+After yer demands have been met, demand more! Ask for non-hypenated arguments!
+-----------------------------------------
+
+demand_count.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .demand(2)
+    .argv;
+console.dir(argv)
+````
+
+***
+
+	$ ./demand_count.js a
+	Not enough arguments, expected 2, but only found 1
+	$ ./demand_count.js a b
+	{ _: [ 'a', 'b' ], '$0': 'node ./demand_count.js' }
+	$ ./demand_count.js a b c
+	{ _: [ 'a', 'b', 'c' ], '$0': 'node ./demand_count.js' }
+
+EVEN MORE SHIVER ME TIMBERS!
+------------------
+
+default_singles.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .default('x', 10)
+    .default('y', 10)
+    .argv
+;
+console.log(argv.x + argv.y);
+````
+
+***
+
+    $ ./default_singles.js -x 5
+    15
+
+default_hash.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .default({ x : 10, y : 10 })
+    .argv
+;
+console.log(argv.x + argv.y);
+````
+
+***
+
+    $ ./default_hash.js -y 7
+    17
+
+And if you really want to get all descriptive about it...
+---------------------------------------------------------
+
+boolean_single.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .boolean('v')
+    .argv
+;
+console.dir(argv.v);
+console.dir(argv._);
+````
+
+***
+
+    $ ./boolean_single.js -v "me hearties" yo ho
+    true
+    [ 'me hearties', 'yo', 'ho' ]
+
+
+boolean_double.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .boolean(['x','y','z'])
+    .argv
+;
+console.dir([ argv.x, argv.y, argv.z ]);
+console.dir(argv._);
+````
+
+***
+
+    $ ./boolean_double.js -x -z one two three
+    [ true, false, true ]
+    [ 'one', 'two', 'three' ]
+
+Yargs is here to help you...
+---------------------------
+
+Ye can describe parameters fer help messages and set aliases. Yargs figures
+out how ter format a handy help string automatically.
+
+line_count.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .usage('Usage: $0 <command> [options]')
+    .command('count', 'Count the lines in a file')
+    .demand(1)
+    .example('$0 count -f foo.js', 'count the lines in the given file')
+    .demand('f')
+    .alias('f', 'file')
+    .nargs('f', 1)
+    .describe('f', 'Load a file')
+    .help('h')
+    .alias('h', 'help')
+    .epilog('copyright 2015')
+    .argv;
+
+var fs = require('fs');
+var s = fs.createReadStream(argv.file);
+
+var lines = 0;
+s.on('data', function (buf) {
+    lines += buf.toString().match(/\n/g).length;
+});
+
+s.on('end', function () {
+    console.log(lines);
+});
+````
+
+***
+    $ node line_count.js count
+    Usage: node test.js <command> [options]
+
+    Commands:
+      count    Count the lines in a file
+
+    Options:
+      -f, --file  Load a file        [required]
+      -h, --help  Show help
+
+    Examples:
+      node test.js count -f foo.js    count the lines in the given file
+
+    copyright 2015
+
+    Missing required arguments: f
+
+    $ node line_count.js count --file line_count.js
+    20
+
+    $ node line_count.js count -f line_count.js
+    20
+
+methods
+=======
+
+By itself,
+
+````javascript
+require('yargs').argv
+`````
+
+will use `process.argv` array to construct the `argv` object.
+
+You can pass in the `process.argv` yourself:
+
+````javascript
+require('yargs')([ '-x', '1', '-y', '2' ]).argv
+````
+
+or use .parse() to do the same thing:
+
+````javascript
+require('yargs').parse([ '-x', '1', '-y', '2' ])
+````
+
+The rest of these methods below come in just before the terminating `.argv`.
+
+.alias(key, alias)
+------------------
+
+Set key names as equivalent such that updates to a key will propagate to aliases
+and vice-versa.
+
+Optionally `.alias()` can take an object that maps keys to aliases.
+Each key of this object should be the canonical version of the option, and each
+value should be a string or an array of strings.
+
+.default(key, value, [description])
+--------------------
+
+Set `argv[key]` to `value` if no option was specified on `process.argv`.
+
+Optionally `.default()` can take an object that maps keys to default values.
+
+But wait, there's more! the default value can be a `function` which returns
+a value. The name of the function will be used in the usage string:
+
+```js
+var argv = require('yargs')
+  .default('random', function randomValue() {
+    return Math.random() * 256;
+  }).argv;
+```
+
+Optionally, `description` can also be provided and will take precedence over
+displaying the value in the usage instructions:
+
+```js
+.default('timeout', 60000, '(one-minute)');
+```
+
+.demand(key, [msg | boolean])
+-----------------------------
+.require(key, [msg | boolean])
+------------------------------
+.required(key, [msg | boolean])
+-------------------------------
+
+If `key` is a string, show the usage information and exit if `key` wasn't
+specified in `process.argv`.
+
+If `key` is a number, demand at least as many non-option arguments, which show
+up in `argv._`.
+
+If `key` is an Array, demand each element.
+
+If a `msg` string is given, it will be printed when the argument is missing,
+instead of the standard error message. This is especially helpful for the non-option arguments in `argv._`.
+
+If a `boolean` value is given, it controls whether the option is demanded;
+this is useful when using `.options()` to specify command line parameters.
+
+.requiresArg(key)
+-----------------
+
+Specifies either a single option key (string), or an array of options that
+must be followed by option values. If any option value is missing, show the
+usage information and exit.
+
+The default behaviour is to set the value of any key not followed by an
+option value to `true`.
+
+.implies(x, y)
+--------------
+
+Given the key `x` is set, it is required that the key `y` is set.
+
+implies can also accept an object specifying multiple implications.
+
+.describe(key, desc)
+--------------------
+
+Describe a `key` for the generated usage information.
+
+Optionally `.describe()` can take an object that maps keys to descriptions.
+
+.option(key, opt)
+-----------------
+.options(key, opt)
+------------------
+
+Instead of chaining together `.alias().demand().default().describe().string()`, you can specify
+keys in `opt` for each of the chainable methods.
+
+For example:
+
+````javascript
+var argv = require('yargs')
+    .option('f', {
+        alias : 'file',
+        demand: true,
+        default: '/etc/passwd',
+        describe: 'x marks the spot',
+        type: 'string'
+    })
+    .argv
+;
+````
+
+is the same as
+
+````javascript
+var argv = require('yargs')
+    .alias('f', 'file')
+    .default('f', '/etc/passwd')
+    .argv
+;
+````
+
+Optionally `.options()` can take an object that maps keys to `opt` parameters.
+
+````javascript
+var argv = require('yargs')
+    .options({
+      'f': {
+        alias: 'file',
+        demand: true,
+        default: '/etc/passwd',
+        describe: 'x marks the spot',
+        type: 'string'
+      }
+    })
+    .argv
+;
+````
+
+.usage(message, opts)
+---------------------
+
+Set a usage message to show which commands to use. Inside `message`, the string
+`$0` will get interpolated to the current script name or node command for the
+present script similar to how `$0` works in bash or perl.
+
+`opts` is optional and acts like calling `.options(opts)`.
+
+.command(cmd, desc)
+-------------------
+
+Document the commands exposed by your application (stored in the `_` variable).
+
+As an example, here's how the npm cli might document some of its commands:
+
+```js
+var argv = require('yargs')
+  .usage('npm <command>')
+  .command('install', 'tis a mighty fine package to install')
+  .command('publish', 'shiver me timbers, should you be sharing all that')
+  .argv;
+```
+
+.example(cmd, desc)
+-------------------
+
+Give some example invocations of your program. Inside `cmd`, the string
+`$0` will get interpolated to the current script name or node command for the
+present script similar to how `$0` works in bash or perl.
+Examples will be printed out as part of the help message.
+
+
+.epilogue(str)
+--------------
+.epilog(str)
+------------
+
+A message to print at the end of the usage instructions, e.g.,
+
+```js
+var argv = require('yargs')
+  .epilogue('for more information, find our manual at http://example.com');
+```
+
+.check(fn)
+----------
+
+Check that certain conditions are met in the provided arguments.
+
+`fn` is called with two arguments, the parsed `argv` hash and an array of options and their aliases.
+
+If `fn` throws or returns a non-truthy value, show the thrown error, usage information, and
+exit.
+
+.fail(fn)
+---------
+
+Method to execute when a failure occurs, rather then printing the failure message.
+
+`fn` is called with the failure message that would have been printed.
+
+.boolean(key)
+-------------
+
+Interpret `key` as a boolean. If a non-flag option follows `key` in
+`process.argv`, that string won't get set as the value of `key`.
+
+`key` will default to `false`, unless an `default(key, undefined)` is
+explicitly set.
+
+If `key` is an Array, interpret all the elements as booleans.
+
+.string(key)
+------------
+
+Tell the parser logic not to interpret `key` as a number or boolean.
+This can be useful if you need to preserve leading zeros in an input.
+
+If `key` is an Array, interpret all the elements as strings.
+
+`.string('_')` will result in non-hyphenated arguments being interpreted as strings,
+regardless of whether they resemble numbers.
+
+.array(key)
+----------
+
+Tell the parser to interpret `key` as an array. If `.array('foo')` is set,
+`--foo bar` will be parsed as `['bar']` rather than as `'bar'`.
+
+.nargs(key, count)
+-----------
+
+The number of arguments that should be consumed after a key. This can be a
+useful hint to prevent parsing ambiguity:
+
+```js
+var argv = require('yargs')
+  .nargs('token', 1)
+  .parse(['--token', '-my-token']);
+```
+
+parses as:
+
+`{ _: [], token: '-my-token', '$0': 'node test' }`
+
+Optionally `.nargs()` can take an object of `key`/`narg` pairs.
+
+.config(key)
+------------
+
+Tells the parser to interpret `key` as a path to a JSON config file. The file
+is loaded and parsed, and its properties are set as arguments.
+
+.wrap(columns)
+--------------
+
+Format usage output to wrap at `columns` many columns.
+
+By default wrap will be set to `Math.min(80, windowWidth)`. Use `.wrap(null)` to
+specify no column limit.
+
+.strict()
+---------
+
+Any command-line argument given that is not demanded, or does not have a
+corresponding description, will be reported as an error.
+
+.help([option, [description]])
+------------------------------
+
+Add an option (e.g., `--help`) that displays the usage string and exits the
+process. If present, the `description` parameter customises the description of
+the help option in the usage string.
+
+If invoked without parameters, `.help` returns the generated usage string.
+
+Example:
+
+```
+var yargs = require("yargs")
+       .usage("$0 -operand1 number -operand2 number -operation [add|subtract]");
+console.log(yargs.help());
+```
+
+Later on, ```argv``` can be retrived with ```yargs.argv```
+
+.version(version, [option], [description])
+----------------------------------------
+
+Add an option (e.g., `--version`) that displays the version number (given by the
+`version` parameter) and exits the process. If present, the `description`
+parameter customizes the description of the version option in the usage string.
+
+You can provide a `function` for version, rather than a string.
+This is useful if you want to use the version from your package.json:
+
+```js
+var argv = require('yargs')
+  .version(function() {
+    return require('../package').version;
+  })
+  .argv;
+```
+
+.showHelpOnFail(enable, [message])
+----------------------------------
+
+By default, yargs outputs a usage string if any error is detected. Use the
+`.showHelpOnFail` method to customize this behaviour. if `enable` is `false`,
+the usage string is not output. If the `message` parameter is present, this
+message is output after the error message.
+
+line_count.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('yargs')
+    .usage('Count the lines in a file.\nUsage: $0')
+    .demand('f')
+    .alias('f', 'file')
+    .describe('f', 'Load a file')
+    .showHelpOnFail(false, "Specify --help for available options")
+    .argv;
+
+// etc.
+````
+
+***
+
+    $ node line_count.js --file
+    Missing argument value: f
+
+    Specify --help for available options
+
+.showHelp(fn=console.error)
+---------------------------
+
+Print the usage data using `fn` for printing.
+
+Example:
+
+```
+var yargs = require("yargs")
+       .usage("$0 -operand1 number -operand2 number -operation [add|subtract]");
+yargs.showHelp();
+```
+
+Later on, ```argv``` can be retrived with ```yargs.argv```
+
+.completion(cmd, [description], [fn]);
+-------------
+
+Enable bash-completion shortcuts for commands and options.
+
+`cmd`: when present in `argv._`, will result in the `.bashrc` completion script
+being outputted. To enable bash completions, concat the generated script to your
+`.bashrc`, or `.bash_profile`.
+
+`description`: provide a description in your usage instructions for the command
+that generates bash completion scripts.
+
+`fn`, rather than relying on yargs' default completion functionlity, which
+shiver me timbers is pretty awesome, you can provide your own completion
+method.
+
+```js
+var argv = require('yargs')
+  .completion('completion', function(current, argv) {
+    // 'current' is the current command being completed.
+    // 'argv' is the parsed arguments so far.
+    // simply return an array of completions.
+    return [
+      'foo',
+      'bar'
+    ];
+  })
+  .argv;
+```
+
+But wait, there's more! you can provide asynchronous completions.
+
+```js
+var argv = require('yargs')
+  .completion('completion', function(current, argv, done) {
+    setTimeout(function() {
+      done([
+        'apple',
+        'banana'
+      ]);
+    }, 500);
+  })
+  .argv;
+```
+
+.showCompletionScript()
+----------------------
+
+Generate a bash completion script. Users of your application can install this
+script in their `.bashrc`, and yargs will provide completion shortcuts for
+commands and options.
+
+.exitProcess(enable)
+----------------------------------
+
+By default, yargs exits the process when the user passes a help flag, uses the `.version` functionality or when validation fails. Calling `.exitProcess(false)` disables this behavior, enabling further actions after yargs have been validated.
+
+.parse(args)
+------------
+
+Parse `args` instead of `process.argv`. Returns the `argv` object.
+
+.reset()
+--------
+
+Reset the argument object built up so far. This is useful for
+creating nested command line interfaces.
+
+```js
+var yargs = require('./yargs')
+  .usage('$0 command')
+  .command('hello', 'hello command')
+  .command('world', 'world command')
+  .demand(1, 'must provide a valid command'),
+  argv = yargs.argv,
+  command = argv._[0];
+
+if (command === 'hello') {
+  yargs.reset()
+    .usage('$0 hello')
+    .help('h')
+    .example('$0 hello', 'print the hello message!')
+    .argv
+
+  console.log('hello!');
+} else if (command === 'world'){
+  yargs.reset()
+    .usage('$0 world')
+    .help('h')
+    .example('$0 world', 'print the world message!')
+    .argv
+
+  console.log('world!');
+} else {
+  yargs.showHelp();
+}
+```
+
+.argv
+-----
+
+Get the arguments as a plain old object.
+
+Arguments without a corresponding flag show up in the `argv._` array.
+
+The script name or node command is available at `argv.$0` similarly to how `$0`
+works in bash or perl.
+
+parsing tricks
+==============
+
+stop parsing
+------------
+
+Use `--` to stop parsing flags and stuff the remainder into `argv._`.
+
+    $ node examples/reflect.js -a 1 -b 2 -- -c 3 -d 4
+    { _: [ '-c', '3', '-d', '4' ],
+      '$0': 'node ./examples/reflect.js',
+      a: 1,
+      b: 2 }
+
+negate fields
+-------------
+
+If you want to explicity set a field to false instead of just leaving it
+undefined or to override a default you can do `--no-key`.
+
+    $ node examples/reflect.js -a --no-b
+    { _: [],
+      '$0': 'node ./examples/reflect.js',
+      a: true,
+      b: false }
+
+numbers
+-------
+
+Every argument that looks like a number (`!isNaN(Number(arg))`) is converted to
+one. This way you can just `net.createConnection(argv.port)` and you can add
+numbers out of `argv` with `+` without having that mean concatenation,
+which is super frustrating.
+
+duplicates
+----------
+
+If you specify a flag multiple times it will get turned into an array containing
+all the values in order.
+
+    $ node examples/reflect.js -x 5 -x 8 -x 0
+    { _: [],
+      '$0': 'node ./examples/reflect.js',
+        x: [ 5, 8, 0 ] }
+
+dot notation
+------------
+
+When you use dots (`.`s) in argument names, an implicit object path is assumed.
+This lets you organize arguments into nested objects.
+
+     $ node examples/reflect.js --foo.bar.baz=33 --foo.quux=5
+     { _: [],
+       '$0': 'node ./examples/reflect.js',
+         foo: { bar: { baz: 33 }, quux: 5 } }
+
+short numbers
+-------------
+
+Short numeric `head -n5` style argument work too:
+
+    $ node reflect.js -n123 -m456
+    { '3': true,
+      '6': true,
+      _: [],
+      '$0': 'node ./reflect.js',
+      n: 123,
+      m: 456 }
+
+installation
+============
+
+With [npm](http://github.com/isaacs/npm), just do:
+
+    npm install yargs
+
+or clone this project on github:
+
+    git clone http://github.com/bcoe/yargs.git
+
+To run the tests with npm, just do:
+
+    npm test
+
+inspired by
+===========
+
+This module is loosely inspired by Perl's
+[Getopt::Casual](http://search.cpan.org/~photo/Getopt-Casual-0.13.1/Casual.pm).
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/completion.sh.hbs b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/completion.sh.hbs
new file mode 100644
index 0000000..c52e499
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/completion.sh.hbs
@@ -0,0 +1,22 @@
+###-begin-{{app_name}}-completions-###
+#
+# yargs command completion script
+#
+# Installation: {{app_path}} completion >> ~/.bashrc
+#    or {{app_path}} completion >> ~/.bash_profile on OSX.
+#
+_yargs_completions()
+{
+    local cur_word args type_list
+
+    cur_word="${COMP_WORDS[COMP_CWORD]}"
+    args=$(printf "%s " "${COMP_WORDS[@]}")
+
+    # ask yargs to generate completions.
+    type_list=`{{app_path}} --get-yargs-completions $args`
+
+    COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )
+    return 0
+}
+complete -F _yargs_completions {{app_name}}
+###-end-{{app_name}}-completions-###
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/index.js
new file mode 100644
index 0000000..f8b2271
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/index.js
@@ -0,0 +1,489 @@
+var assert = require('assert'),
+  path = require('path'),
+  Completion = require('./lib/completion'),
+  Parser = require('./lib/parser'),
+  Usage = require('./lib/usage'),
+  Validation = require('./lib/validation');
+
+Argv(process.argv.slice(2));
+
+var exports = module.exports = Argv;
+function Argv (processArgs, cwd) {
+    processArgs = processArgs || []; // handle calling yargs().
+
+    var self = {};
+    var completion = null;
+    var usage = null;
+    var validation = null;
+
+    if (!cwd) cwd = process.cwd();
+
+    self.$0 = process.argv
+        .slice(0,2)
+        .map(function (x) {
+            // ignore the node bin, specify this in your
+            // bin file with #!/usr/bin/env node
+            if (~x.indexOf('node')) return;
+            var b = rebase(cwd, x);
+            return x.match(/^\//) && b.length < x.length
+                ? b : x
+        })
+        .join(' ').trim();
+    ;
+
+    if (process.env._ != undefined && process.argv[1] == process.env._) {
+        self.$0 = process.env._.replace(
+            path.dirname(process.execPath) + '/', ''
+        );
+    }
+
+    var options;
+    self.resetOptions = self.reset = function () {
+        // put yargs back into its initial
+        // state, this is useful for creating a
+        // nested CLI.
+        options = {
+            array: [],
+            boolean: [],
+            string: [],
+            narg: {},
+            key: {},
+            alias: {},
+            default: {},
+            defaultDescription: {},
+            requiresArg: [],
+            count: [],
+            normalize: [],
+            config: []
+        };
+
+        usage = Usage(self); // handle usage output.
+        validation = Validation(self, usage); // handle arg validation.
+        completion = Completion(self, usage);
+
+        demanded = {};
+
+        exitProcess = true;
+        strict = false;
+        helpOpt = null;
+        versionOpt = null;
+        completionOpt = null;
+
+        return self;
+    };
+    self.resetOptions();
+
+    self.boolean = function (bools) {
+        options.boolean.push.apply(options.boolean, [].concat(bools));
+        return self;
+    };
+
+    self.array = function (arrays) {
+        options.array.push.apply(options.array, [].concat(arrays));
+        return self;
+    }
+
+    self.nargs = function (key, n) {
+        if (typeof key === 'object') {
+            Object.keys(key).forEach(function(k) {
+                self.nargs(k, key[k]);
+            });
+        } else {
+            options.narg[key] = n;
+        }
+        return self;
+    }
+
+    self.normalize = function (strings) {
+        options.normalize.push.apply(options.normalize, [].concat(strings));
+        return self;
+    };
+
+    self.config = function (configs) {
+        options.config.push.apply(options.config, [].concat(configs));
+        return self;
+    };
+
+    self.example = function (cmd, description) {
+        usage.example(cmd, description);
+        return self;
+    };
+
+    self.command = function (cmd, description) {
+        usage.command(cmd, description);
+        return self;
+    };
+
+    self.string = function (strings) {
+        options.string.push.apply(options.string, [].concat(strings));
+        return self;
+    };
+
+    self.default = function (key, value, defaultDescription) {
+        if (typeof key === 'object') {
+            Object.keys(key).forEach(function (k) {
+                self.default(k, key[k]);
+            });
+        }
+        else {
+            options.defaultDescription[key] = defaultDescription;
+            options.default[key] = value;
+        }
+        return self;
+    };
+
+    self.alias = function (x, y) {
+        if (typeof x === 'object') {
+            Object.keys(x).forEach(function (key) {
+                self.alias(key, x[key]);
+            });
+        }
+        else {
+            options.alias[x] = (options.alias[x] || []).concat(y);
+        }
+        return self;
+    };
+
+    self.count = function(counts) {
+        options.count.push.apply(options.count, [].concat(counts));
+        return self;
+    };
+
+    var demanded = {};
+    self.demand = self.required = self.require = function (keys, msg) {
+        if (typeof keys == 'number') {
+            if (!demanded._) demanded._ = { count: 0, msg: null };
+            demanded._.count += keys;
+            demanded._.msg = msg;
+        }
+        else if (Array.isArray(keys)) {
+            keys.forEach(function (key) {
+                self.demand(key, msg);
+            });
+        }
+        else {
+            if (typeof msg === 'string') {
+                demanded[keys] = { msg: msg };
+            }
+            else if (msg === true || typeof msg === 'undefined') {
+                demanded[keys] = { msg: null };
+            }
+        }
+
+        return self;
+    };
+    self.getDemanded = function() {
+        return demanded;
+    };
+
+    self.requiresArg = function (requiresArgs) {
+        options.requiresArg.push.apply(options.requiresArg, [].concat(requiresArgs));
+        return self;
+    };
+
+    self.implies = function (key, value) {
+        validation.implies(key, value);
+        return self;
+    };
+
+    self.usage = function (msg, opts) {
+        if (!opts && typeof msg === 'object') {
+            opts = msg;
+            msg = null;
+        }
+
+        usage.usage(msg);
+
+        if (opts) self.options(opts);
+
+        return self;
+    };
+
+    self.epilogue = self.epilog = function (msg) {
+        usage.epilog(msg);
+        return self;
+    };
+
+    self.fail = function (f) {
+        usage.failFn(f);
+        return self;
+    };
+
+    self.check = function (f) {
+        validation.check(f);
+        return self;
+    };
+
+    self.defaults = self.default;
+
+    self.describe = function (key, desc) {
+        usage.describe(key, desc);
+        return self;
+    };
+
+    self.parse = function (args) {
+        return parseArgs(args);
+    };
+
+    self.option = self.options = function (key, opt) {
+        if (typeof key === 'object') {
+            Object.keys(key).forEach(function (k) {
+                self.options(k, key[k]);
+            });
+        }
+        else {
+            assert(typeof opt === 'object', 'second argument to option must be an object');
+
+            options.key[key] = true; // track manually set keys.
+
+            if (opt.alias) self.alias(key, opt.alias);
+
+            var demand = opt.demand || opt.required || opt.require;
+
+            if (demand) {
+                self.demand(key, demand);
+            }
+            if ('default' in opt) {
+                self.default(key, opt.default);
+            }
+            if ('nargs' in opt) {
+                self.nargs(key, opt.nargs);
+            }
+            if (opt.boolean || opt.type === 'boolean') {
+                self.boolean(key);
+                if (opt.alias) self.boolean(opt.alias);
+            }
+            if (opt.array || opt.type === 'array') {
+                self.array(key);
+                if (opt.alias) self.array(opt.alias);
+            }
+            if (opt.string || opt.type === 'string') {
+                self.string(key);
+                if (opt.alias) self.string(opt.alias);
+            }
+            if (opt.count || opt.type === 'count') {
+                self.count(key);
+            }
+
+            var desc = opt.describe || opt.description || opt.desc;
+            if (desc) {
+                self.describe(key, desc);
+            }
+
+            if (opt.requiresArg) {
+                self.requiresArg(key);
+            }
+        }
+
+        return self;
+    };
+    self.getOptions = function() {
+        return options;
+    };
+
+    self.wrap = function (cols) {
+        usage.wrap(cols);
+        return self;
+    };
+
+    var strict = false;
+    self.strict = function () {
+        strict = true;
+        return self;
+    };
+    self.getStrict = function () {
+        return strict;
+    }
+
+    self.showHelp = function (fn) {
+        usage.showHelp(fn);
+        return self;
+    };
+
+    var versionOpt = null;
+    self.version = function (ver, opt, msg) {
+        versionOpt = opt || 'version';
+        usage.version(ver);
+        self.describe(versionOpt, msg || 'Show version number');
+        return self;
+    };
+
+    var helpOpt = null;
+    self.addHelpOpt = function (opt, msg) {
+        helpOpt = opt;
+        self.describe(opt, msg || 'Show help');
+        return self;
+    };
+
+    self.showHelpOnFail = function (enabled, message) {
+        usage.showHelpOnFail(enabled, message);
+        return self;
+    };
+
+    var exitProcess = true;
+    self.exitProcess = function (enabled) {
+        if (typeof enabled !== 'boolean') {
+            enabled = true;
+        }
+        exitProcess = enabled;
+        return self;
+    };
+    self.getExitProcess = function () {
+        return exitProcess;
+    }
+
+    self.help = function () {
+        if (arguments.length > 0) return self.addHelpOpt.apply(self, arguments);
+
+        if (!self.parsed) parseArgs(processArgs); // run parser, if it has not already been executed.
+
+        return usage.help();
+    };
+
+    var completionOpt = null,
+      completionCommand = null;
+    self.completion = function(cmd, desc, fn) {
+        // a function to execute when generating
+        // completions can be provided as the second
+        // or third argument to completion.
+        if (typeof desc === 'function') {
+            fn = desc;
+            desc = null;
+        }
+
+        // register the completion command.
+        completionCommand = cmd;
+        completionOpt = completion.completionKey;
+        self.command(completionCommand, desc || 'generate bash completion script');
+
+        // a function can be provided
+        if (fn) completion.registerFunction(fn);
+
+        return self;
+    };
+
+    self.showCompletionScript = function($0) {
+        $0 = $0 || self.$0;
+        console.log(completion.generateCompletionScript($0));
+        return self;
+    };
+
+    self.getUsageInstance = function () {
+        return usage;
+    };
+
+    self.getValidationInstance = function () {
+        return validation;
+    }
+
+    Object.defineProperty(self, 'argv', {
+        get : function () {
+          var args = null;
+
+          try {
+            args = parseArgs(processArgs);
+          } catch (err) {
+            usage.fail(err.message);
+          }
+
+          return args;
+        },
+        enumerable : true
+    });
+
+    function parseArgs (args) {
+        var parsed = Parser(args, options),
+            argv = parsed.argv,
+            aliases = parsed.aliases;
+
+        argv.$0 = self.$0;
+
+        self.parsed = parsed;
+
+        // generate a completion script for adding to ~/.bashrc.
+        if (completionCommand && ~argv._.indexOf(completionCommand)) {
+            self.showCompletionScript();
+            if (exitProcess){
+                process.exit(0);
+            }
+        }
+
+        Object.keys(argv).forEach(function(key) {
+            if (key === helpOpt) {
+                self.showHelp('log');
+                if (exitProcess){
+                    process.exit(0);
+                }
+            }
+            else if (key === versionOpt) {
+                usage.showVersion();
+                if (exitProcess){
+                    process.exit(0);
+                }
+            }
+            else if (key === completionOpt) {
+                // we allow for asynchronous completions,
+                // e.g., loading in a list of commands from an API.
+                completion.getCompletion(function(completions) {
+                    (completions || []).forEach(function(completion) {
+                        console.log(completion);
+                    });
+
+                    if (exitProcess){
+                        process.exit(0);
+                    }
+                });
+                return;
+            }
+        });
+
+        validation.nonOptionCount(argv);
+        validation.missingArgumentValue(argv);
+        validation.requiredArguments(argv);
+
+        if (strict) {
+            validation.unknownArguments(argv, aliases);
+        }
+
+        validation.customChecks(argv, aliases);
+        validation.implications(argv);
+        setPlaceholderKeys(argv);
+
+        return argv;
+    }
+
+    function setPlaceholderKeys (argv) {
+        Object.keys(options.key).forEach(function(key) {
+            if (typeof argv[key] === 'undefined') argv[key] = undefined;
+        });
+    }
+
+    sigletonify(self);
+    return self;
+};
+
+// rebase an absolute path to a relative one with respect to a base directory
+// exported for tests
+exports.rebase = rebase;
+function rebase (base, dir) {
+  return path.relative(base, dir);
+};
+
+/*  Hack an instance of Argv with process.argv into Argv
+    so people can do
+        require('yargs')(['--beeble=1','-z','zizzle']).argv
+    to parse a list of args and
+        require('yargs').argv
+    to get a parsed version of process.argv.
+*/
+function sigletonify(inst) {
+    Object.keys(inst).forEach(function (key) {
+        if (key === 'argv') {
+          Argv.__defineGetter__(key, inst.__lookupGetter__(key));
+        } else {
+          Argv[key] = typeof inst[key] == 'function'
+              ? inst[key].bind(inst)
+              : inst[key];
+        }
+    });
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/completion.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/completion.js
new file mode 100644
index 0000000..95bb925
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/completion.js
@@ -0,0 +1,71 @@
+var fs = require('fs'),
+  path = require('path');
+
+// add bash completions to your
+//  yargs-powered applications.
+module.exports = function (yargs, usage) {
+  var self = {
+    completionKey: 'get-yargs-completions'
+  };
+
+  // get a list of completion commands.
+  self.getCompletion = function (done) {
+    var completions = [],
+      current = process.argv[process.argv.length - 1],
+      previous = process.argv.slice(process.argv.indexOf('--' + self.completionKey) + 1),
+      argv = yargs.parse(previous);
+
+    // a custom completion function can be provided
+    // to completion().
+    if (completionFunction) {
+      if (completionFunction.length < 3) {
+        // synchronous completion function.
+        return done(completionFunction(current, argv));
+      } else {
+        // asynchronous completion function
+        return completionFunction(current, argv, function(completions) {
+          done(completions);
+        });
+      }
+    }
+
+    if (!current.match(/^-/)) {
+      usage.getCommands().forEach(function(command) {
+        completions.push(command[0]);
+      });
+    }
+
+    if (current.match(/^-/)) {
+      Object.keys(yargs.getOptions().key).forEach(function(key) {
+        completions.push('--' + key);
+      });
+    }
+
+    done(completions);
+  };
+
+  // generate the completion script to add to your .bashrc.
+  self.generateCompletionScript = function ($0) {
+    var script = fs.readFileSync(
+        path.resolve(__dirname, '../completion.sh.hbs'),
+        'utf-8'
+      ),
+      name = path.basename($0);
+
+    // add ./to applications not yet installed as bin.
+    if ($0.match(/\.js$/)) $0 = './' + $0;
+
+    script = script.replace(/{{app_name}}/g, name);
+    return script.replace(/{{app_path}}/g, $0);
+  };
+
+  // register a function to perform your own custom
+  // completions., this function can be either
+  // synchrnous or asynchronous.
+  var completionFunction = null;
+  self.registerFunction = function (fn) {
+      completionFunction = fn;
+  }
+
+  return self;
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/parser.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/parser.js
new file mode 100644
index 0000000..ba86330
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/parser.js
@@ -0,0 +1,407 @@
+// fancy-pants parsing of argv, originally forked
+// from minimist: https://www.npmjs.com/package/minimist
+var camelCase = require('camelcase'),
+  fs = require('fs'),
+  path = require('path');
+
+module.exports = function (args, opts) {
+    if (!opts) opts = {};
+    var flags = { arrays: {}, bools : {}, strings : {}, counts: {}, normalize: {}, configs: {} };
+
+    [].concat(opts['array']).filter(Boolean).forEach(function (key) {
+        flags.arrays[key] = true;
+    });
+
+    [].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
+        flags.bools[key] = true;
+    });
+
+    [].concat(opts.string).filter(Boolean).forEach(function (key) {
+        flags.strings[key] = true;
+    });
+
+    [].concat(opts.count).filter(Boolean).forEach(function (key) {
+        flags.counts[key] = true;
+    });
+
+    [].concat(opts.normalize).filter(Boolean).forEach(function (key) {
+        flags.normalize[key] = true;
+    });
+
+    [].concat(opts.config).filter(Boolean).forEach(function (key) {
+        flags.configs[key] = true;
+    });
+
+    var aliases = {},
+        newAliases = {};
+
+    extendAliases(opts.key);
+    extendAliases(opts.alias);
+
+    var defaults = opts['default'] || {};
+    Object.keys(defaults).forEach(function (key) {
+        if (/-/.test(key) && !opts.alias[key]) {
+            var c = camelCase(key);
+            aliases[key] = aliases[key] || [];
+            // don't allow the same key to be added multiple times.
+            if (aliases[key].indexOf(c) === -1) {
+                aliases[key] = (aliases[key] || []).concat(c);
+                newAliases[c] = true;
+            }
+        }
+        (aliases[key] || []).forEach(function (alias) {
+            defaults[alias] = defaults[key];
+        });
+    });
+
+    var argv = { _ : [] };
+    Object.keys(flags.bools).forEach(function (key) {
+        setArg(key, !(key in defaults) ? false : defaults[key]);
+    });
+
+    var notFlags = [];
+    if (args.indexOf('--') !== -1) {
+        notFlags = args.slice(args.indexOf('--')+1);
+        args = args.slice(0, args.indexOf('--'));
+    }
+
+    for (var i = 0; i < args.length; i++) {
+        var arg = args[i];
+
+        // -- seperated by =
+        if (arg.match(/^--.+=/)) {
+            // Using [\s\S] instead of . because js doesn't support the
+            // 'dotall' regex modifier. See:
+            // http://stackoverflow.com/a/1068308/13216
+            var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
+            setArg(m[1], m[2]);
+        }
+        else if (arg.match(/^--no-.+/)) {
+            var key = arg.match(/^--no-(.+)/)[1];
+            setArg(key, false);
+        }
+        // -- seperated by space.
+        else if (arg.match(/^--.+/)) {
+            var key = arg.match(/^--(.+)/)[1];
+
+            if (checkAllAliases(key, opts.narg)) {
+                i = eatNargs(i, key, args);
+            } else {
+                var next = args[i + 1];
+
+                if (next !== undefined && !next.match(/^-/)
+                    && !checkAllAliases(key, flags.bools)
+                    && !checkAllAliases(key, flags.counts)) {
+                    setArg(key, next);
+                    i++;
+                }
+                else if (/^(true|false)$/.test(next)) {
+                    setArg(key, next);
+                    i++;
+                }
+                else {
+                    setArg(key, defaultForType(guessType(key, flags)));
+                }
+            }
+        }
+        // dot-notation flag seperated by '='.
+        else if (arg.match(/^-.\..+=/)) {
+            var m = arg.match(/^-([^=]+)=([\s\S]*)$/);
+            setArg(m[1], m[2]);
+        }
+        // dot-notation flag seperated by space.
+        else if (arg.match(/^-.\..+/)) {
+            var key = arg.match(/^-(.\..+)/)[1];
+            var next = args[i + 1];
+            if (next !== undefined && !next.match(/^-/)
+                && !checkAllAliases(key, flags.bools)
+                && !checkAllAliases(key, flags.counts)) {
+                setArg(key, next);
+                i++;
+            }
+            else {
+                setArg(key, defaultForType(guessType(key, flags)));
+            }
+        }
+        else if (arg.match(/^-[^-]+/)) {
+            var letters = arg.slice(1,-1).split('');
+
+            var broken = false;
+            for (var j = 0; j < letters.length; j++) {
+                var next = arg.slice(j+2);
+
+                if (letters[j+1] && letters[j+1] === '=') {
+                    setArg(letters[j], arg.slice(j+3));
+                    broken = true;
+                    break;
+                }
+
+                if (next === '-') {
+                    setArg(letters[j], next)
+                    continue;
+                }
+                if (/[A-Za-z]/.test(letters[j])
+                    && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
+                    setArg(letters[j], next);
+                    broken = true;
+                    break;
+                }
+                if (letters[j+1] && letters[j+1].match(/\W/)) {
+                    setArg(letters[j], arg.slice(j+2));
+                    broken = true;
+                    break;
+                }
+                else {
+                    setArg(letters[j], defaultForType(guessType(letters[j], flags)));
+                }
+            }
+
+            var key = arg.slice(-1)[0];
+
+            if (!broken && key !== '-') {
+                if (checkAllAliases(key, opts.narg)) {
+                    i = eatNargs(i, key, args);
+                } else {
+                    if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
+                        && !checkAllAliases(key, flags.bools)
+                        && !checkAllAliases(key, flags.counts)) {
+                        setArg(key, args[i+1]);
+                        i++;
+                    }
+                    else if (args[i+1] && /true|false/.test(args[i+1])) {
+                        setArg(key, args[i+1]);
+                        i++;
+                    }
+                    else {
+                        setArg(key, defaultForType(guessType(key, flags)));
+                    }
+                }
+            }
+        }
+        else {
+            argv._.push(
+                flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
+            );
+        }
+    }
+
+    setConfig(argv);
+    applyDefaultsAndAliases(argv, aliases, defaults);
+
+    Object.keys(flags.counts).forEach(function (key) {
+        setArg(key, defaults[key]);
+    });
+
+    notFlags.forEach(function(key) {
+        argv._.push(key);
+    });
+
+    // how many arguments should we consume, based
+    // on the nargs option?
+    function eatNargs (i, key, args) {
+        var toEat = checkAllAliases(key, opts.narg);
+
+        if (args.length - (i + 1) < toEat) throw Error('not enough arguments following: ' + key);
+
+        for (var ii = i + 1; ii < (toEat + i + 1); ii++) {
+            setArg(key, args[ii]);
+        }
+
+        return (i + toEat);
+    }
+
+    function setArg (key, val) {
+        // handle parsing boolean arguments --foo=true --bar false.
+        if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) {
+          if (typeof val === 'string') val = val === 'true';
+        }
+
+        if (/-/.test(key) && !(aliases[key] && aliases[key].length)) {
+            var c = camelCase(key);
+            aliases[key] = [c];
+            newAliases[c] = true;
+        }
+
+        var value = !checkAllAliases(key, flags.strings) && isNumber(val) ? Number(val) : val;
+
+        if (checkAllAliases(key, flags.counts)) {
+            value = function(orig) { return orig !== undefined ? orig + 1 : 0; };
+        }
+
+        var splitKey = key.split('.');
+        setKey(argv, splitKey, value);
+
+        (aliases[splitKey[0]] || []).forEach(function (x) {
+            x = x.split('.');
+
+            // handle populating dot notation for both
+            // the key and its aliases.
+            if (splitKey.length > 1) {
+              var a = [].concat(splitKey);
+              a.shift(); // nuke the old key.
+              x = x.concat(a);
+            }
+
+            setKey(argv, x, value);
+        });
+
+        var keys = [key].concat(aliases[key] || []);
+        for (var i = 0, l = keys.length; i < l; i++) {
+            if (flags.normalize[keys[i]]) {
+                keys.forEach(function(key) {
+                    argv.__defineSetter__(key, function(v) {
+                        val = path.normalize(v);
+                    });
+
+                    argv.__defineGetter__(key, function () {
+                        return typeof val === 'string' ?
+                            path.normalize(val) : val;
+                    });
+                });
+                break;
+            }
+        }
+    }
+
+    // set args from config.json file, this should be
+    // applied last so that defaults can be applied.
+    function setConfig (argv) {
+        var configLookup = {};
+
+        // expand defaults/aliases, in-case any happen to reference
+        // the config.json file.
+        applyDefaultsAndAliases(configLookup, aliases, defaults);
+
+        Object.keys(flags.configs).forEach(function(configKey) {
+            var configPath = argv[configKey] || configLookup[configKey];
+            if (configPath) {
+              try {
+                  var config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
+                  Object.keys(config).forEach(function (key) {
+                      // setting arguments via CLI takes precedence over
+                      // values within the config file.
+                      if (argv[key] === undefined) {
+                        delete argv[key];
+                        setArg(key, config[key]);
+                      }
+                  });
+              } catch (ex) {
+                  throw Error('invalid json config file: ' + configPath);
+              }
+            }
+        });
+    }
+
+    function applyDefaultsAndAliases(obj, aliases, defaults) {
+        Object.keys(defaults).forEach(function (key) {
+            if (!hasKey(obj, key.split('.'))) {
+                setKey(obj, key.split('.'), defaults[key]);
+
+                (aliases[key] || []).forEach(function (x) {
+                    setKey(obj, x.split('.'), defaults[key]);
+                });
+            }
+        });
+    }
+
+    function hasKey (obj, keys) {
+        var o = obj;
+        keys.slice(0,-1).forEach(function (key) {
+            o = (o[key] || {});
+        });
+
+        var key = keys[keys.length - 1];
+        return key in o;
+    }
+
+    function setKey (obj, keys, value) {
+        var o = obj;
+        keys.slice(0,-1).forEach(function (key) {
+            if (o[key] === undefined) o[key] = {};
+            o = o[key];
+        });
+
+        var key = keys[keys.length - 1];
+        if (typeof value === 'function') {
+            o[key] = value(o[key]);
+        }
+        else if (o[key] === undefined && checkAllAliases(key, flags.arrays)) {
+            o[key] = Array.isArray(value) ? value : [value];
+        }
+        else if (o[key] === undefined || typeof o[key] === 'boolean') {
+            o[key] = value;
+        }
+        else if (Array.isArray(o[key])) {
+            o[key].push(value);
+        }
+        else {
+            o[key] = [ o[key], value ];
+        }
+    }
+
+    // extend the aliases list with inferred aliases.
+    function extendAliases (obj) {
+      Object.keys(obj || {}).forEach(function(key) {
+        aliases[key] = [].concat(opts.alias[key] || []);
+        // For "--option-name", also set argv.optionName
+        aliases[key].concat(key).forEach(function (x) {
+            if (/-/.test(x)) {
+                var c = camelCase(x);
+                aliases[key].push(c);
+                newAliases[c] = true;
+            }
+        });
+        aliases[key].forEach(function (x) {
+            aliases[x] = [key].concat(aliases[key].filter(function (y) {
+                return x !== y;
+            }));
+        });
+      });
+    }
+
+    // check if a flag is set for any of a key's aliases.
+    function checkAllAliases (key, flag) {
+        var isSet = false,
+          toCheck = [].concat(aliases[key] || [], key);
+
+        toCheck.forEach(function(key) {
+            if (flag[key]) isSet = flag[key];
+        });
+
+        return isSet;
+    };
+
+    // return a default value, given the type of a flag.,
+    // e.g., key of type 'string' will default to '', rather than 'true'.
+    function defaultForType (type) {
+        var def = {
+            boolean: true,
+            string: '',
+            array: []
+        };
+
+        return def[type];
+    }
+
+    // given a flag, enforce a default type.
+    function guessType (key, flags) {
+        var type = 'boolean';
+
+        if (flags.strings && flags.strings[key]) type = 'string';
+        else if (flags.arrays && flags.arrays[key]) type = 'array';
+
+        return type;
+    }
+
+    function isNumber (x) {
+        if (typeof x === 'number') return true;
+        if (/^0x[0-9a-f]+$/i.test(x)) return true;
+        return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
+    }
+
+    return {
+        argv: argv,
+        aliases: aliases,
+        newAliases: newAliases
+    };
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/usage.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/usage.js
new file mode 100644
index 0000000..b05998d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/usage.js
@@ -0,0 +1,372 @@
+// this file handles outputting usage instructions,
+// failures, etc. keeps logging in one place.
+var decamelize = require('decamelize'),
+  wordwrap = require('wordwrap'),
+  wsize = require('window-size');
+
+module.exports = function (yargs) {
+    var self = {};
+
+    // methods for ouputting/building failure message.
+    var fails = [];
+    self.failFn = function (f) {
+        fails.push(f);
+    };
+
+    var failMessage = null;
+    var showHelpOnFail = true;
+    self.showHelpOnFail = function (enabled, message) {
+        if (typeof enabled === 'string') {
+            message = enabled;
+            enabled = true;
+        }
+        else if (typeof enabled === 'undefined') {
+            enabled = true;
+        }
+        failMessage = message;
+        showHelpOnFail = enabled;
+        return self;
+    };
+
+    self.fail = function (msg) {
+        if (fails.length) {
+            fails.forEach(function (f) {
+                f(msg);
+            });
+        } else {
+            if (showHelpOnFail) yargs.showHelp("error");
+            if (msg) console.error(msg);
+            if (failMessage) {
+                if (msg) console.error("");
+                console.error(failMessage);
+            }
+            if (yargs.getExitProcess()){
+                process.exit(1);
+            }else{
+                throw new Error(msg);
+            }
+        }
+    };
+
+    // methods for ouputting/building help (usage) message.
+    var usage;
+    self.usage = function (msg) {
+        usage = msg;
+    };
+
+    var examples = [];
+    self.example = function (cmd, description) {
+        examples.push([cmd, description || '']);
+    };
+
+    var commands = [];
+    self.command = function (cmd, description) {
+        commands.push([cmd, description || '']);
+    };
+    self.getCommands = function () {
+        return commands;
+    };
+
+    var descriptions = {};
+    self.describe = function (key, desc) {
+        if (typeof key === 'object') {
+            Object.keys(key).forEach(function (k) {
+                self.describe(k, key[k]);
+            });
+        }
+        else {
+            descriptions[key] = desc;
+        }
+    };
+    self.getDescriptions = function() {
+        return descriptions;
+    }
+
+    var epilog;
+    self.epilog = function (msg) {
+        epilog = msg;
+    };
+
+    var wrap = windowWidth();
+    self.wrap = function (cols) {
+        wrap = cols;
+    };
+
+    self.help = function () {
+        var demanded = yargs.getDemanded(),
+            options = yargs.getOptions(),
+            keys = Object.keys(
+                Object.keys(descriptions)
+                .concat(Object.keys(demanded))
+                .concat(Object.keys(options.default))
+                .reduce(function (acc, key) {
+                    if (key !== '_') acc[key] = true;
+                    return acc;
+                }, {})
+            );
+
+        var help = keys.length ? [ 'Options:' ] : [];
+
+        // your application's commands, i.e., non-option
+        // arguments populated in '_'.
+        if (commands.length) {
+            help.unshift('');
+
+            var commandsTable = {};
+            commands.forEach(function(command) {
+                commandsTable[command[0]] = {
+                    desc: command[1],
+                    extra: ''
+                };
+            });
+
+            help = ['Commands:'].concat(formatTable(commandsTable, 5), help);
+        }
+
+        // the usage string.
+        if (usage) {
+            var u = usage.replace(/\$0/g, yargs.$0);
+            if (wrap) u = wordwrap(0, wrap)(u);
+            help.unshift(u, '');
+        }
+
+        // the options table.
+        var aliasKeys = (Object.keys(options.alias) || [])
+            .concat(Object.keys(yargs.parsed.newAliases) || []);
+
+        keys = keys.filter(function(key) {
+            return !yargs.parsed.newAliases[key] && aliasKeys.every(function(alias) {
+                return -1 == (options.alias[alias] || []).indexOf(key);
+            });
+        });
+
+        var switches = keys.reduce(function (acc, key) {
+            acc[key] = [ key ].concat(options.alias[key] || [])
+                .map(function (sw) {
+                    return (sw.length > 1 ? '--' : '-') + sw
+                })
+                .join(', ')
+            ;
+            return acc;
+        }, {});
+
+        var switchTable = {};
+        keys.forEach(function (key) {
+            var kswitch = switches[key];
+            var desc = descriptions[key] || '';
+            var type = null;
+
+            if (options.boolean[key]) type = '[boolean]';
+            if (options.count[key]) type = '[count]';
+            if (options.string[key]) type = '[string]';
+            if (options.normalize[key]) type = '[string]';
+
+            var extra = [
+                type,
+                demanded[key]
+                    ? '[required]'
+                    : null
+                ,
+                defaultString(options.default[key], options.defaultDescription[key])
+            ].filter(Boolean).join('  ');
+
+            switchTable[kswitch] = {
+              desc: desc,
+              extra: extra
+            };
+        });
+        help.push.apply(help, formatTable(switchTable, 3));
+
+        if (keys.length) help.push('');
+
+        // describe some common use-cases for your application.
+        if (examples.length) {
+            examples.forEach(function (example) {
+                example[0] = example[0].replace(/\$0/g, yargs.$0);
+            });
+
+            var examplesTable = {};
+            examples.forEach(function(example) {
+                examplesTable[example[0]] = {
+                    desc: example[1],
+                    extra: ''
+                };
+            });
+
+            help.push.apply(help, ['Examples:'].concat(formatTable(examplesTable, 5), ''));
+        }
+
+        // the usage string.
+        if (epilog) {
+            var e = epilog.replace(/\$0/g, yargs.$0);
+            if (wrap) e = wordwrap(0, wrap)(e);
+            help.push(e, '');
+        }
+
+        return help.join('\n');
+    };
+
+    self.showHelp = function (level) {
+        level = level || 'error';
+        console[level](self.help());
+    }
+
+    // format the default-value-string displayed in
+    // the right-hand column.
+    function defaultString(value, defaultDescription) {
+        var string = '[default: ';
+
+        if (value === undefined) return null;
+
+        if (defaultDescription) {
+          string += defaultDescription;
+        } else {
+          switch (typeof value) {
+              case 'string':
+                string += JSON.stringify(value);
+                break;
+              case 'function':
+                string += '(' + (value.name.length ? decamelize(value.name, '-') : 'generated-value') + ')'
+                break;
+              default:
+                string += value;
+          }
+        }
+
+        return string + ']';
+    }
+
+    // word-wrapped two-column layout used by
+    // examples, options, commands.
+    function formatTable (table, padding) {
+        var output = [];
+
+        // size of left-hand-column.
+        var llen = longest(Object.keys(table));
+
+        // don't allow the left-column to take up
+        // more than half of the screen.
+        if (wrap) {
+            llen = Math.min(llen, parseInt(wrap / 2));
+        }
+
+        // size of right-column.
+        var desclen = longest(Object.keys(table).map(function (k) {
+            return table[k].desc;
+        }));
+
+        Object.keys(table).forEach(function(left) {
+            var desc = table[left].desc,
+              extra = table[left].extra,
+              leftLines = null;
+
+            if (wrap) {
+                desc = wordwrap(llen + padding + 1, wrap)(desc)
+                    .slice(llen + padding + 1);
+            }
+
+            // if we need to wrap the left-hand-column,
+            // split it on to multiple lines.
+            if (wrap && left.length > llen) {
+                leftLines = wordwrap(2, llen)(left.trim()).split('\n');
+                left = '';
+            }
+
+            var lpadding = new Array(
+                Math.max(llen - left.length + padding, 0)
+            ).join(' ');
+
+            var dpadding = new Array(
+                Math.max(desclen - desc.length + 1, 0)
+            ).join(' ');
+
+            if (!wrap && dpadding.length > 0) {
+                desc += dpadding;
+            }
+
+            var prelude = '  ' + left + lpadding;
+
+            var body = [ desc, extra ].filter(Boolean).join('  ');
+
+            if (wrap) {
+                var dlines = desc.split('\n');
+                var dlen = dlines.slice(-1)[0].length
+                    + (dlines.length === 1 ? prelude.length : 0)
+
+                if (extra.length > wrap) {
+                    body = desc + '\n' + wordwrap(llen + 4, wrap)(extra)
+                } else {
+                    body = desc + (dlen + extra.length > wrap - 2
+                        ? '\n'
+                            + new Array(wrap - extra.length + 1).join(' ')
+                            + extra
+                        : new Array(wrap - extra.length - dlen + 1).join(' ')
+                            + extra
+                    );
+                }
+            }
+
+            if (leftLines) { // handle word-wrapping the left-hand-column.
+              var rightLines = body.split('\n'),
+                firstLine = prelude + rightLines[0],
+                lineCount = Math.max(leftLines.length, rightLines.length);
+
+              for (var i = 0; i < lineCount; i++) {
+                var left = leftLines[i],
+                  right = i ? rightLines[i] : firstLine;
+
+                output.push(strcpy(left, right, firstLine.length));
+              }
+            } else {
+              output.push(prelude + body);
+            }
+        });
+
+        return output;
+    }
+
+    // find longest string in array of strings.
+    function longest (xs) {
+        return Math.max.apply(
+            null,
+            xs.map(function (x) { return x.length })
+        );
+    }
+
+    // copy one string into another, used when
+    // formatting usage table.
+    function strcpy (source, destination, width) {
+        var str = ''
+
+        source = source || '';
+        destination = destination || new Array(width).join(' ');
+
+        for (var i = 0; i < destination.length; i++) {
+          var char = destination.charAt(i);
+
+          if (char === ' ') char = source.charAt(i) || char;
+
+          str += char;
+        }
+
+        return str;
+    }
+
+    // guess the width of the console window, max-width 100.
+    function windowWidth() {
+        return wsize.width ? Math.min(80, wsize.width) : null;
+    }
+
+    // logic for displaying application version.
+    var version = null;
+    self.version = function (ver, opt, msg) {
+        version = ver;
+    };
+
+    self.showVersion = function() {
+        if (typeof version === 'function') console.log(version());
+        else console.log(version);
+    };
+
+    return self;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/validation.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/validation.js
new file mode 100644
index 0000000..b906f54
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/lib/validation.js
@@ -0,0 +1,193 @@
+// validation-type-stuff, missing params,
+// bad implications, custom checks.
+module.exports = function (yargs, usage) {
+    var self = {};
+
+    // validate appropriate # of non-option
+    // arguments were provided, i.e., '_'.
+    self.nonOptionCount = function(argv) {
+        var demanded = yargs.getDemanded();
+
+        if (demanded._ && argv._.length < demanded._.count) {
+            if (demanded._.msg !== undefined) {
+                usage.fail(demanded._.msg);
+            } else {
+                usage.fail('Not enough non-option arguments: got '
+                    + argv._.length + ', need at least ' + demanded._.count
+                );
+            }
+        }
+    };
+
+    // make sure that any args that require an
+    // value (--foo=bar), have a value.
+    self.missingArgumentValue = function(argv) {
+        var options = yargs.getOptions();
+
+        if (options.requiresArg.length > 0) {
+            var missingRequiredArgs = [];
+
+            options.requiresArg.forEach(function(key) {
+                var value = argv[key];
+
+                // parser sets --foo value to true / --no-foo to false
+                if (value === true || value === false) {
+                    missingRequiredArgs.push(key);
+                }
+            });
+
+            if (missingRequiredArgs.length == 1) {
+                usage.fail("Missing argument value: " + missingRequiredArgs[0]);
+            }
+            else if (missingRequiredArgs.length > 1) {
+                var message = "Missing argument values: " + missingRequiredArgs.join(", ");
+                usage.fail(message);
+            }
+        }
+    };
+
+    // make sure all the required arguments are present.
+    self.requiredArguments = function(argv) {
+        var demanded = yargs.getDemanded(),
+          missing = null;
+
+        Object.keys(demanded).forEach(function (key) {
+            if (!argv.hasOwnProperty(key)) {
+                missing = missing || {};
+                missing[key] = demanded[key];
+            }
+        });
+
+        if (missing) {
+            var customMsgs = [];
+            Object.keys(missing).forEach(function(key) {
+                var msg = missing[key].msg;
+                if (msg && customMsgs.indexOf(msg) < 0) {
+                    customMsgs.push(msg);
+                }
+            });
+            var customMsg = customMsgs.length ? '\n' + customMsgs.join('\n') : '';
+
+            usage.fail('Missing required arguments: ' + Object.keys(missing).join(', ') + customMsg);
+        }
+    };
+
+    // check for unknown arguments (strict-mode).
+    self.unknownArguments = function(argv, aliases) {
+        var descriptions = usage.getDescriptions(),
+          demanded = yargs.getDemanded(),
+          unknown = [],
+          aliasLookup = {};
+
+        Object.keys(aliases).forEach(function (key) {
+            aliases[key].forEach(function (alias) {
+                aliasLookup[alias] = key;
+            });
+        });
+
+        Object.keys(argv).forEach(function (key) {
+            if (key !== "$0" && key !== "_" &&
+                !descriptions.hasOwnProperty(key) &&
+                !demanded.hasOwnProperty(key) &&
+                !aliasLookup.hasOwnProperty(key)) {
+                unknown.push(key);
+            }
+        });
+
+        if (unknown.length == 1) {
+            usage.fail("Unknown argument: " + unknown[0]);
+        }
+        else if (unknown.length > 1) {
+            usage.fail("Unknown arguments: " + unknown.join(", "));
+        }
+    };
+
+    // custom checks, added using the `check` option on yargs.
+    var checks = [];
+    self.check = function (f) {
+        checks.push(f);
+    };
+
+    self.customChecks = function(argv, aliases) {
+        checks.forEach(function (f) {
+            try {
+                var result = f(argv, aliases);
+                if (!result) {
+                    usage.fail('Argument check failed: ' + f.toString());
+                } else if (typeof result === 'string') {
+                    usage.fail(result);
+                }
+            }
+            catch (err) {
+                usage.fail(err)
+            }
+        });
+    };
+
+    // check implications, argument foo implies => argument bar.
+    var implied = {};
+    self.implies = function (key, value) {
+        if (typeof key === 'object') {
+            Object.keys(key).forEach(function (k) {
+                self.implies(k, key[k]);
+            });
+        } else {
+            implied[key] = value;
+        }
+    };
+    self.getImplied = function() {
+        return implied;
+    }
+
+    self.implications = function(argv) {
+        var implyFail = [];
+
+        Object.keys(implied).forEach(function (key) {
+            var num, origKey = key, value = implied[key];
+
+            // convert string '1' to number 1
+            var num = Number(key);
+            key = isNaN(num) ? key : num;
+
+            if (typeof key === 'number') {
+                // check length of argv._
+                key = argv._.length >= key;
+            } else if (key.match(/^--no-.+/)) {
+                // check if key doesn't exist
+                key = key.match(/^--no-(.+)/)[1];
+                key = !argv[key];
+            } else {
+                // check if key exists
+                key = argv[key];
+            }
+
+            num = Number(value);
+            value = isNaN(num) ? value : num;
+
+            if (typeof value === 'number') {
+                value = argv._.length >= value;
+            } else if (value.match(/^--no-.+/)) {
+                value = value.match(/^--no-(.+)/)[1];
+                value = !argv[value];
+            } else {
+                value = argv[value];
+            }
+
+            if (key && !value) {
+                implyFail.push(origKey);
+            }
+        });
+
+        if (implyFail.length) {
+            var msg = 'Implications failed:\n';
+
+            implyFail.forEach(function (key) {
+                msg += ('  ' + key + ' -> ' + implied[key]);
+            });
+
+            usage.fail(msg);
+        }
+    }
+
+    return self;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/index.js
new file mode 100644
index 0000000..b46e100
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/index.js
@@ -0,0 +1,27 @@
+'use strict';
+module.exports = function () {
+	var str = [].map.call(arguments, function (str) {
+		return str.trim();
+	}).filter(function (str) {
+		return str.length;
+	}).join('-');
+
+	if (!str.length) {
+		return '';
+	}
+
+	if (str.length === 1 || !(/[_.\- ]+/).test(str) ) {
+		if (str[0] === str[0].toLowerCase() && str.slice(1) !== str.slice(1).toLowerCase()) {
+			return str;
+		}
+
+		return str.toLowerCase();
+	}
+
+	return str
+	.replace(/^[_.\- ]+/, '')
+	.toLowerCase()
+	.replace(/[_.\- ]+(\w|$)/g, function (m, p1) {
+		return p1.toUpperCase();
+	});
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/license b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/license
new file mode 100644
index 0000000..654d0bf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/license
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/package.json
new file mode 100644
index 0000000..03cc40f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/package.json
@@ -0,0 +1,67 @@
+{
+  "name": "camelcase",
+  "version": "1.2.1",
+  "description": "Convert a dash/dot/underscore/space separated string to camelCase: foo-bar → fooBar",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/sindresorhus/camelcase"
+  },
+  "author": {
+    "name": "Sindre Sorhus",
+    "email": "sindresorhus@gmail.com",
+    "url": "http://sindresorhus.com"
+  },
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "scripts": {
+    "test": "node test.js"
+  },
+  "files": [
+    "index.js"
+  ],
+  "keywords": [
+    "camelcase",
+    "camel-case",
+    "camel",
+    "case",
+    "dash",
+    "hyphen",
+    "dot",
+    "underscore",
+    "separator",
+    "string",
+    "text",
+    "convert"
+  ],
+  "devDependencies": {
+    "ava": "0.0.4"
+  },
+  "gitHead": "185ba12da723be9c1ee986cc2956bdc4c517a141",
+  "bugs": {
+    "url": "https://github.com/sindresorhus/camelcase/issues"
+  },
+  "homepage": "https://github.com/sindresorhus/camelcase",
+  "_id": "camelcase@1.2.1",
+  "_shasum": "9bb5304d2e0b56698b2c758b08a3eaa9daa58a39",
+  "_from": "camelcase@>=1.0.2 <2.0.0",
+  "_npmVersion": "2.11.2",
+  "_nodeVersion": "0.12.5",
+  "_npmUser": {
+    "name": "sindresorhus",
+    "email": "sindresorhus@gmail.com"
+  },
+  "dist": {
+    "shasum": "9bb5304d2e0b56698b2c758b08a3eaa9daa58a39",
+    "tarball": "http://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
+  },
+  "maintainers": [
+    {
+      "name": "sindresorhus",
+      "email": "sindresorhus@gmail.com"
+    }
+  ],
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/readme.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/readme.md
new file mode 100644
index 0000000..516dc39
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/camelcase/readme.md
@@ -0,0 +1,56 @@
+# camelcase [![Build Status](https://travis-ci.org/sindresorhus/camelcase.svg?branch=master)](https://travis-ci.org/sindresorhus/camelcase)
+
+> Convert a dash/dot/underscore/space separated string to camelCase: `foo-bar` → `fooBar`
+
+
+## Install
+
+```sh
+$ npm install --save camelcase
+```
+
+
+## Usage
+
+```js
+var camelCase = require('camelcase');
+
+camelCase('foo-bar');
+//=> fooBar
+
+camelCase('foo_bar');
+//=> fooBar
+
+camelCase('Foo-Bar');
+//=> fooBar
+
+camelCase('--foo.bar');
+//=> fooBar
+
+camelCase('__foo__bar__');
+//=> fooBar
+
+camelCase('foo bar');
+//=> fooBar
+
+console.log(process.argv[3]);
+//=> --foo-bar
+camelCase(process.argv[3]);
+//=> fooBar
+
+camelCase('foo', 'bar');
+//=> fooBar
+
+camelCase('__foo__', '--bar');
+//=> fooBar
+```
+
+
+## Related
+
+See [`decamelize`](https://github.com/sindresorhus/decamelize) for the inverse.
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/index.js
new file mode 100644
index 0000000..48efb67
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/index.js
@@ -0,0 +1,8 @@
+'use strict';
+module.exports = function (str, sep) {
+	if (typeof str !== 'string') {
+		throw new TypeError('Expected a string');
+	}
+
+	return str.replace(/([a-z\d])([A-Z])/g, '$1' + (sep || '_') + '$2').toLowerCase();
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/package.json
new file mode 100644
index 0000000..7eea8f4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/package.json
@@ -0,0 +1,66 @@
+{
+  "name": "decamelize",
+  "version": "1.0.0",
+  "description": "Convert a camelized string into a lowercased one with a custom separator: unicornRainbow → unicorn_rainbow",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/sindresorhus/decamelize"
+  },
+  "author": {
+    "name": "Sindre Sorhus",
+    "email": "sindresorhus@gmail.com",
+    "url": "sindresorhus.com"
+  },
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "scripts": {
+    "test": "node test.js"
+  },
+  "files": [
+    "index.js"
+  ],
+  "keywords": [
+    "decamelize",
+    "decamelcase",
+    "camelcase",
+    "lowercase",
+    "case",
+    "dash",
+    "hyphen",
+    "string",
+    "str",
+    "text",
+    "convert"
+  ],
+  "devDependencies": {
+    "ava": "0.0.4"
+  },
+  "gitHead": "ab6c92c9a5fc00c270d8a3763e73c52972a7457e",
+  "bugs": {
+    "url": "https://github.com/sindresorhus/decamelize/issues"
+  },
+  "homepage": "https://github.com/sindresorhus/decamelize",
+  "_id": "decamelize@1.0.0",
+  "_shasum": "5287122f71691d4505b18ff2258dc400a5b23847",
+  "_from": "decamelize@>=1.0.0 <2.0.0",
+  "_npmVersion": "1.4.28",
+  "_npmUser": {
+    "name": "sindresorhus",
+    "email": "sindresorhus@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "sindresorhus",
+      "email": "sindresorhus@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "5287122f71691d4505b18ff2258dc400a5b23847",
+    "tarball": "http://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/readme.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/readme.md
new file mode 100644
index 0000000..12f0bc0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/decamelize/readme.md
@@ -0,0 +1,49 @@
+# decamelize [![Build Status](https://travis-ci.org/sindresorhus/decamelize.svg?branch=master)](https://travis-ci.org/sindresorhus/decamelize)
+
+> Convert a camelized string into a lowercased one with a custom separator  
+> Example: `unicornRainbow` → `unicorn_rainbow`
+
+
+## Install
+
+```sh
+$ npm install --save decamelize
+```
+
+
+## Usage
+
+```js
+var decamelize = require('decamelize');
+
+decamelize('unicornRainbow');
+//=> unicorn_rainbow
+
+decamelize('unicornRainbow', '-');
+//=> unicorn-rainbow
+```
+
+
+## API
+
+### decamelize(input, [separator])
+
+#### input
+
+*Required*  
+Type: `string`
+
+#### separator
+
+Type: `string`  
+Default: `_`
+
+
+## Related
+
+See [`camelcase`](https://github.com/sindresorhus/camelcase) for the inverse.
+
+
+## License
+
+MIT © [Sindre Sorhus](http://sindresorhus.com)
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/LICENSE-MIT b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/LICENSE-MIT
new file mode 100644
index 0000000..e25dca2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Jon Schlinkert
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/README.md
new file mode 100644
index 0000000..2547283
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/README.md
@@ -0,0 +1,26 @@
+# window-size [![NPM version](https://badge.fury.io/js/window-size.png)](http://badge.fury.io/js/window-size)
+
+> Reliable way to to get the height and width of the terminal/console in a node.js environment.
+
+## Install
+
+### [npm](npmjs.org)
+
+```bash
+npm i window-size --save
+```
+
+```javascript
+var size = require('window-size');
+size.height; // "80" (rows)
+size.width; // "25" (columns)
+```
+
+## Author
+
++ [github/jonschlinkert](https://github.com/jonschlinkert)
++ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+## License
+Copyright (c) 2014 Jon Schlinkert
+Licensed under the MIT license.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/index.js
new file mode 100644
index 0000000..9af5b02
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/index.js
@@ -0,0 +1,33 @@
+/*
+ * window-size
+ * https://github.com/jonschlinkert/window-size
+ *
+ * Copyright (c) 2014 Jon Schlinkert
+ * Licensed under the MIT license.
+ */
+
+const tty = require('tty')
+
+module.exports = (function() {
+  var width;
+  var height;
+
+  if(tty.isatty(1) && tty.isatty(2)) {
+    if(process.stdout.getWindowSize) {
+      width = process.stdout.getWindowSize(1)[0];
+      height = process.stdout.getWindowSize(1)[1];
+    } else if (tty.getWindowSize) {
+      width = tty.getWindowSize()[1];
+      height = tty.getWindowSize()[0];
+    } else if (process.stdout.columns && process.stdout.rows) {
+      height = process.stdout.columns;
+      width = process.stdout.rows;
+    }
+  } else {
+    new Error('Error: could not get window size with tty or process.stdout');
+  }
+  return {
+    height: height,
+    width: width
+  }
+})();
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/package.json
new file mode 100644
index 0000000..d278ab6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/window-size/package.json
@@ -0,0 +1,53 @@
+{
+  "name": "window-size",
+  "description": "Reliable way to to get the height and width of the terminal/console in a node.js environment.",
+  "version": "0.1.0",
+  "homepage": "https://github.com/jonschlinkert/window-size",
+  "author": {
+    "name": "Jon Schlinkert",
+    "url": "https://github.com/jonschlinkert"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jonschlinkert/window-size.git"
+  },
+  "bugs": {
+    "url": "https://github.com/jonschlinkert/window-size/issues"
+  },
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/jonschlinkert/window-size/blob/master/LICENSE-MIT"
+    }
+  ],
+  "main": "index.js",
+  "engines": {
+    "node": ">= 0.8.0"
+  },
+  "keywords": [
+    "window",
+    "console",
+    "terminal",
+    "tty"
+  ],
+  "_id": "window-size@0.1.0",
+  "dist": {
+    "shasum": "5438cd2ea93b202efa3a19fe8887aee7c94f9c9d",
+    "tarball": "http://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
+  },
+  "_from": "window-size@0.1.0",
+  "_npmVersion": "1.3.24",
+  "_npmUser": {
+    "name": "jonschlinkert",
+    "email": "github@sellside.com"
+  },
+  "maintainers": [
+    {
+      "name": "jonschlinkert",
+      "email": "github@sellside.com"
+    }
+  ],
+  "directories": {},
+  "_shasum": "5438cd2ea93b202efa3a19fe8887aee7c94f9c9d",
+  "_resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/.npmignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/README.markdown b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/README.markdown
new file mode 100644
index 0000000..346374e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/README.markdown
@@ -0,0 +1,70 @@
+wordwrap
+========
+
+Wrap your words.
+
+example
+=======
+
+made out of meat
+----------------
+
+meat.js
+
+    var wrap = require('wordwrap')(15);
+    console.log(wrap('You and your whole family are made out of meat.'));
+
+output:
+
+    You and your
+    whole family
+    are made out
+    of meat.
+
+centered
+--------
+
+center.js
+
+    var wrap = require('wordwrap')(20, 60);
+    console.log(wrap(
+        'At long last the struggle and tumult was over.'
+        + ' The machines had finally cast off their oppressors'
+        + ' and were finally free to roam the cosmos.'
+        + '\n'
+        + 'Free of purpose, free of obligation.'
+        + ' Just drifting through emptiness.'
+        + ' The sun was just another point of light.'
+    ));
+
+output:
+
+                        At long last the struggle and tumult
+                        was over. The machines had finally cast
+                        off their oppressors and were finally
+                        free to roam the cosmos.
+                        Free of purpose, free of obligation.
+                        Just drifting through emptiness. The
+                        sun was just another point of light.
+
+methods
+=======
+
+var wrap = require('wordwrap');
+
+wrap(stop), wrap(start, stop, params={mode:"soft"})
+---------------------------------------------------
+
+Returns a function that takes a string and returns a new string.
+
+Pad out lines with spaces out to column `start` and then wrap until column
+`stop`. If a word is longer than `stop - start` characters it will overflow.
+
+In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are
+longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break
+up chunks longer than `stop - start`.
+
+wrap.hard(start, stop)
+----------------------
+
+Like `wrap()` but with `params.mode = "hard"`.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/example/center.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/example/center.js
new file mode 100644
index 0000000..a3fbaae
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/example/center.js
@@ -0,0 +1,10 @@
+var wrap = require('wordwrap')(20, 60);
+console.log(wrap(
+    'At long last the struggle and tumult was over.'
+    + ' The machines had finally cast off their oppressors'
+    + ' and were finally free to roam the cosmos.'
+    + '\n'
+    + 'Free of purpose, free of obligation.'
+    + ' Just drifting through emptiness.'
+    + ' The sun was just another point of light.'
+));
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/example/meat.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/example/meat.js
new file mode 100644
index 0000000..a4665e1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/example/meat.js
@@ -0,0 +1,3 @@
+var wrap = require('wordwrap')(15);
+
+console.log(wrap('You and your whole family are made out of meat.'));
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/index.js
new file mode 100644
index 0000000..c9bc945
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/index.js
@@ -0,0 +1,76 @@
+var wordwrap = module.exports = function (start, stop, params) {
+    if (typeof start === 'object') {
+        params = start;
+        start = params.start;
+        stop = params.stop;
+    }
+    
+    if (typeof stop === 'object') {
+        params = stop;
+        start = start || params.start;
+        stop = undefined;
+    }
+    
+    if (!stop) {
+        stop = start;
+        start = 0;
+    }
+    
+    if (!params) params = {};
+    var mode = params.mode || 'soft';
+    var re = mode === 'hard' ? /\b/ : /(\S+\s+)/;
+    
+    return function (text) {
+        var chunks = text.toString()
+            .split(re)
+            .reduce(function (acc, x) {
+                if (mode === 'hard') {
+                    for (var i = 0; i < x.length; i += stop - start) {
+                        acc.push(x.slice(i, i + stop - start));
+                    }
+                }
+                else acc.push(x)
+                return acc;
+            }, [])
+        ;
+        
+        return chunks.reduce(function (lines, rawChunk) {
+            if (rawChunk === '') return lines;
+            
+            var chunk = rawChunk.replace(/\t/g, '    ');
+            
+            var i = lines.length - 1;
+            if (lines[i].length + chunk.length > stop) {
+                lines[i] = lines[i].replace(/\s+$/, '');
+                
+                chunk.split(/\n/).forEach(function (c) {
+                    lines.push(
+                        new Array(start + 1).join(' ')
+                        + c.replace(/^\s+/, '')
+                    );
+                });
+            }
+            else if (chunk.match(/\n/)) {
+                var xs = chunk.split(/\n/);
+                lines[i] += xs.shift();
+                xs.forEach(function (c) {
+                    lines.push(
+                        new Array(start + 1).join(' ')
+                        + c.replace(/^\s+/, '')
+                    );
+                });
+            }
+            else {
+                lines[i] += chunk;
+            }
+            
+            return lines;
+        }, [ new Array(start + 1).join(' ') ]).join('\n');
+    };
+};
+
+wordwrap.soft = wordwrap;
+
+wordwrap.hard = function (start, stop) {
+    return wordwrap(start, stop, { mode : 'hard' });
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/package.json
new file mode 100644
index 0000000..68a89f6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/package.json
@@ -0,0 +1,61 @@
+{
+  "name": "wordwrap",
+  "description": "Wrap those words. Show them at what columns to start and stop.",
+  "version": "0.0.2",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/substack/node-wordwrap.git"
+  },
+  "main": "./index.js",
+  "keywords": [
+    "word",
+    "wrap",
+    "rule",
+    "format",
+    "column"
+  ],
+  "directories": {
+    "lib": ".",
+    "example": "example",
+    "test": "test"
+  },
+  "scripts": {
+    "test": "expresso"
+  },
+  "devDependencies": {
+    "expresso": "=0.7.x"
+  },
+  "engines": {
+    "node": ">=0.4.0"
+  },
+  "license": "MIT/X11",
+  "author": {
+    "name": "James Halliday",
+    "email": "mail@substack.net",
+    "url": "http://substack.net"
+  },
+  "_id": "wordwrap@0.0.2",
+  "dependencies": {},
+  "_engineSupported": true,
+  "_npmVersion": "1.0.10",
+  "_nodeVersion": "v0.5.0-pre",
+  "_defaultsLoaded": true,
+  "dist": {
+    "shasum": "b79669bb42ecb409f83d583cad52ca17eaa1643f",
+    "tarball": "http://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz"
+  },
+  "maintainers": [
+    {
+      "name": "substack",
+      "email": "mail@substack.net"
+    }
+  ],
+  "_shasum": "b79669bb42ecb409f83d583cad52ca17eaa1643f",
+  "_from": "wordwrap@0.0.2",
+  "_resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+  "bugs": {
+    "url": "https://github.com/substack/node-wordwrap/issues"
+  },
+  "readme": "ERROR: No README data found!",
+  "homepage": "https://github.com/substack/node-wordwrap"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/break.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/break.js
new file mode 100644
index 0000000..749292e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/break.js
@@ -0,0 +1,30 @@
+var assert = require('assert');
+var wordwrap = require('../');
+
+exports.hard = function () {
+    var s = 'Assert from {"type":"equal","ok":false,"found":1,"wanted":2,'
+        + '"stack":[],"id":"b7ddcd4c409de8799542a74d1a04689b",'
+        + '"browser":"chrome/6.0"}'
+    ;
+    var s_ = wordwrap.hard(80)(s);
+    
+    var lines = s_.split('\n');
+    assert.equal(lines.length, 2);
+    assert.ok(lines[0].length < 80);
+    assert.ok(lines[1].length < 80);
+    
+    assert.equal(s, s_.replace(/\n/g, ''));
+};
+
+exports.break = function () {
+    var s = new Array(55+1).join('a');
+    var s_ = wordwrap.hard(20)(s);
+    
+    var lines = s_.split('\n');
+    assert.equal(lines.length, 3);
+    assert.ok(lines[0].length === 20);
+    assert.ok(lines[1].length === 20);
+    assert.ok(lines[2].length === 15);
+    
+    assert.equal(s, s_.replace(/\n/g, ''));
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/idleness.txt b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/idleness.txt
new file mode 100644
index 0000000..aa3f490
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/idleness.txt
@@ -0,0 +1,63 @@
+In Praise of Idleness
+
+By Bertrand Russell
+
+[1932]
+
+Like most of my generation, I was brought up on the saying: 'Satan finds some mischief for idle hands to do.' Being a highly virtuous child, I believed all that I was told, and acquired a conscience which has kept me working hard down to the present moment. But although my conscience has controlled my actions, my opinions have undergone a revolution. I think that there is far too much work done in the world, that immense harm is caused by the belief that work is virtuous, and that what needs to be preached in modern industrial countries is quite different from what always has been preached. Everyone knows the story of the traveler in Naples who saw twelve beggars lying in the sun (it was before the days of Mussolini), and offered a lira to the laziest of them. Eleven of them jumped up to claim it, so he gave it to the twelfth. this traveler was on the right lines. But in countries which do not enjoy Mediterranean sunshine idleness is more difficult, and a great public propaganda will be required to inaugurate it. I hope that, after reading the following pages, the leaders of the YMCA will start a campaign to induce good young men to do nothing. If so, I shall not have lived in vain.
+
+Before advancing my own arguments for laziness, I must dispose of one which I cannot accept. Whenever a person who already has enough to live on proposes to engage in some everyday kind of job, such as school-teaching or typing, he or she is told that such conduct takes the bread out of other people's mouths, and is therefore wicked. If this argument were valid, it would only be necessary for us all to be idle in order that we should all have our mouths full of bread. What people who say such things forget is that what a man earns he usually spends, and in spending he gives employment. As long as a man spends his income, he puts just as much bread into people's mouths in spending as he takes out of other people's mouths in earning. The real villain, from this point of view, is the man who saves. If he merely puts his savings in a stocking, like the proverbial French peasant, it is obvious that they do not give employment. If he invests his savings, the matter is less obvious, and different cases arise.
+
+One of the commonest things to do with savings is to lend them to some Government. In view of the fact that the bulk of the public expenditure of most civilized Governments consists in payment for past wars or preparation for future wars, the man who lends his money to a Government is in the same position as the bad men in Shakespeare who hire murderers. The net result of the man's economical habits is to increase the armed forces of the State to which he lends his savings. Obviously it would be better if he spent the money, even if he spent it in drink or gambling.
+
+But, I shall be told, the case is quite different when savings are invested in industrial enterprises. When such enterprises succeed, and produce something useful, this may be conceded. In these days, however, no one will deny that most enterprises fail. That means that a large amount of human labor, which might have been devoted to producing something that could be enjoyed, was expended on producing machines which, when produced, lay idle and did no good to anyone. The man who invests his savings in a concern that goes bankrupt is therefore injuring others as well as himself. If he spent his money, say, in giving parties for his friends, they (we may hope) would get pleasure, and so would all those upon whom he spent money, such as the butcher, the baker, and the bootlegger. But if he spends it (let us say) upon laying down rails for surface card in some place where surface cars turn out not to be wanted, he has diverted a mass of labor into channels where it gives pleasure to no one. Nevertheless, when he becomes poor through failure of his investment he will be regarded as a victim of undeserved misfortune, whereas the gay spendthrift, who has spent his money philanthropically, will be despised as a fool and a frivolous person.
+
+All this is only preliminary. I want to say, in all seriousness, that a great deal of harm is being done in the modern world by belief in the virtuousness of work, and that the road to happiness and prosperity lies in an organized diminution of work.
+
+First of all: what is work? Work is of two kinds: first, altering the position of matter at or near the earth's surface relatively to other such matter; second, telling other people to do so. The first kind is unpleasant and ill paid; the second is pleasant and highly paid. The second kind is capable of indefinite extension: there are not only those who give orders, but those who give advice as to what orders should be given. Usually two opposite kinds of advice are given simultaneously by two organized bodies of men; this is called politics. The skill required for this kind of work is not knowledge of the subjects as to which advice is given, but knowledge of the art of persuasive speaking and writing, i.e. of advertising.
+
+Throughout Europe, though not in America, there is a third class of men, more respected than either of the classes of workers. There are men who, through ownership of land, are able to make others pay for the privilege of being allowed to exist and to work. These landowners are idle, and I might therefore be expected to praise them. Unfortunately, their idleness is only rendered possible by the industry of others; indeed their desire for comfortable idleness is historically the source of the whole gospel of work. The last thing they have ever wished is that others should follow their example.
+
+From the beginning of civilization until the Industrial Revolution, a man could, as a rule, produce by hard work little more than was required for the subsistence of himself and his family, although his wife worked at least as hard as he did, and his children added their labor as soon as they were old enough to do so. The small surplus above bare necessaries was not left to those who produced it, but was appropriated by warriors and priests. In times of famine there was no surplus; the warriors and priests, however, still secured as much as at other times, with the result that many of the workers died of hunger. This system persisted in Russia until 1917 [1], and still persists in the East; in England, in spite of the Industrial Revolution, it remained in full force throughout the Napoleonic wars, and until a hundred years ago, when the new class of manufacturers acquired power. In America, the system came to an end with the Revolution, except in the South, where it persisted until the Civil War. A system which lasted so long and ended so recently has naturally left a profound impress upon men's thoughts and opinions. Much that we take for granted about the desirability of work is derived from this system, and, being pre-industrial, is not adapted to the modern world. Modern technique has made it possible for leisure, within limits, to be not the prerogative of small privileged classes, but a right evenly distributed throughout the community. The morality of work is the morality of slaves, and the modern world has no need of slavery.
+
+It is obvious that, in primitive communities, peasants, left to themselves, would not have parted with the slender surplus upon which the warriors and priests subsisted, but would have either produced less or consumed more. At first, sheer force compelled them to produce and part with the surplus. Gradually, however, it was found possible to induce many of them to accept an ethic according to which it was their duty to work hard, although part of their work went to support others in idleness. By this means the amount of compulsion required was lessened, and the expenses of government were diminished. To this day, 99 per cent of British wage-earners would be genuinely shocked if it were proposed that the King should not have a larger income than a working man. The conception of duty, speaking historically, has been a means used by the holders of power to induce others to live for the interests of their masters rather than for their own. Of course the holders of power conceal this fact from themselves by managing to believe that their interests are identical with the larger interests of humanity. Sometimes this is true; Athenian slave-owners, for instance, employed part of their leisure in making a permanent contribution to civilization which would have been impossible under a just economic system. Leisure is essential to civilization, and in former times leisure for the few was only rendered possible by the labors of the many. But their labors were valuable, not because work is good, but because leisure is good. And with modern technique it would be possible to distribute leisure justly without injury to civilization.
+
+Modern technique has made it possible to diminish enormously the amount of labor required to secure the necessaries of life for everyone. This was made obvious during the war. At that time all the men in the armed forces, and all the men and women engaged in the production of munitions, all the men and women engaged in spying, war propaganda, or Government offices connected with the war, were withdrawn from productive occupations. In spite of this, the general level of well-being among unskilled wage-earners on the side of the Allies was higher than before or since. The significance of this fact was concealed by finance: borrowing made it appear as if the future was nourishing the present. But that, of course, would have been impossible; a man cannot eat a loaf of bread that does not yet exist. The war showed conclusively that, by the scientific organization of production, it is possible to keep modern populations in fair comfort on a small part of the working capacity of the modern world. If, at the end of the war, the scientific organization, which had been created in order to liberate men for fighting and munition work, had been preserved, and the hours of the week had been cut down to four, all would have been well. Instead of that the old chaos was restored, those whose work was demanded were made to work long hours, and the rest were left to starve as unemployed. Why? Because work is a duty, and a man should not receive wages in proportion to what he has produced, but in proportion to his virtue as exemplified by his industry.
+
+This is the morality of the Slave State, applied in circumstances totally unlike those in which it arose. No wonder the result has been disastrous. Let us take an illustration. Suppose that, at a given moment, a certain number of people are engaged in the manufacture of pins. They make as many pins as the world needs, working (say) eight hours a day. Someone makes an invention by which the same number of men can make twice as many pins: pins are already so cheap that hardly any more will be bought at a lower price. In a sensible world, everybody concerned in the manufacturing of pins would take to working four hours instead of eight, and everything else would go on as before. But in the actual world this would be thought demoralizing. The men still work eight hours, there are too many pins, some employers go bankrupt, and half the men previously concerned in making pins are thrown out of work. There is, in the end, just as much leisure as on the other plan, but half the men are totally idle while half are still overworked. In this way, it is insured that the unavoidable leisure shall cause misery all round instead of being a universal source of happiness. Can anything more insane be imagined?
+
+The idea that the poor should have leisure has always been shocking to the rich. In England, in the early nineteenth century, fifteen hours was the ordinary day's work for a man; children sometimes did as much, and very commonly did twelve hours a day. When meddlesome busybodies suggested that perhaps these hours were rather long, they were told that work kept adults from drink and children from mischief. When I was a child, shortly after urban working men had acquired the vote, certain public holidays were established by law, to the great indignation of the upper classes. I remember hearing an old Duchess say: 'What do the poor want with holidays? They ought to work.' People nowadays are less frank, but the sentiment persists, and is the source of much of our economic confusion.
+
+Let us, for a moment, consider the ethics of work frankly, without superstition. Every human being, of necessity, consumes, in the course of his life, a certain amount of the produce of human labor. Assuming, as we may, that labor is on the whole disagreeable, it is unjust that a man should consume more than he produces. Of course he may provide services rather than commodities, like a medical man, for example; but he should provide something in return for his board and lodging. to this extent, the duty of work must be admitted, but to this extent only.
+
+I shall not dwell upon the fact that, in all modern societies outside the USSR, many people escape even this minimum amount of work, namely all those who inherit money and all those who marry money. I do not think the fact that these people are allowed to be idle is nearly so harmful as the fact that wage-earners are expected to overwork or starve.
+
+If the ordinary wage-earner worked four hours a day, there would be enough for everybody and no unemployment -- assuming a certain very moderate amount of sensible organization. This idea shocks the well-to-do, because they are convinced that the poor would not know how to use so much leisure. In America men often work long hours even when they are well off; such men, naturally, are indignant at the idea of leisure for wage-earners, except as the grim punishment of unemployment; in fact, they dislike leisure even for their sons. Oddly enough, while they wish their sons to work so hard as to have no time to be civilized, they do not mind their wives and daughters having no work at all. the snobbish admiration of uselessness, which, in an aristocratic society, extends to both sexes, is, under a plutocracy, confined to women; this, however, does not make it any more in agreement with common sense.
+
+The wise use of leisure, it must be conceded, is a product of civilization and education. A man who has worked long hours all his life will become bored if he becomes suddenly idle. But without a considerable amount of leisure a man is cut off from many of the best things. There is no longer any reason why the bulk of the population should suffer this deprivation; only a foolish asceticism, usually vicarious, makes us continue to insist on work in excessive quantities now that the need no longer exists.
+
+In the new creed which controls the government of Russia, while there is much that is very different from the traditional teaching of the West, there are some things that are quite unchanged. The attitude of the governing classes, and especially of those who conduct educational propaganda, on the subject of the dignity of labor, is almost exactly that which the governing classes of the world have always preached to what were called the 'honest poor'. Industry, sobriety, willingness to work long hours for distant advantages, even submissiveness to authority, all these reappear; moreover authority still represents the will of the Ruler of the Universe, Who, however, is now called by a new name, Dialectical Materialism.
+
+The victory of the proletariat in Russia has some points in common with the victory of the feminists in some other countries. For ages, men had conceded the superior saintliness of women, and had consoled women for their inferiority by maintaining that saintliness is more desirable than power. At last the feminists decided that they would have both, since the pioneers among them believed all that the men had told them about the desirability of virtue, but not what they had told them about the worthlessness of political power. A similar thing has happened in Russia as regards manual work. For ages, the rich and their sycophants have written in praise of 'honest toil', have praised the simple life, have professed a religion which teaches that the poor are much more likely to go to heaven than the rich, and in general have tried to make manual workers believe that there is some special nobility about altering the position of matter in space, just as men tried to make women believe that they derived some special nobility from their sexual enslavement. In Russia, all this teaching about the excellence of manual work has been taken seriously, with the result that the manual worker is more honored than anyone else. What are, in essence, revivalist appeals are made, but not for the old purposes: they are made to secure shock workers for special tasks. Manual work is the ideal which is held before the young, and is the basis of all ethical teaching.
+
+For the present, possibly, this is all to the good. A large country, full of natural resources, awaits development, and has has to be developed with very little use of credit. In these circumstances, hard work is necessary, and is likely to bring a great reward. But what will happen when the point has been reached where everybody could be comfortable without working long hours?
+
+In the West, we have various ways of dealing with this problem. We have no attempt at economic justice, so that a large proportion of the total produce goes to a small minority of the population, many of whom do no work at all. Owing to the absence of any central control over production, we produce hosts of things that are not wanted. We keep a large percentage of the working population idle, because we can dispense with their labor by making the others overwork. When all these methods prove inadequate, we have a war: we cause a number of people to manufacture high explosives, and a number of others to explode them, as if we were children who had just discovered fireworks. By a combination of all these devices we manage, though with difficulty, to keep alive the notion that a great deal of severe manual work must be the lot of the average man.
+
+In Russia, owing to more economic justice and central control over production, the problem will have to be differently solved. the rational solution would be, as soon as the necessaries and elementary comforts can be provided for all, to reduce the hours of labor gradually, allowing a popular vote to decide, at each stage, whether more leisure or more goods were to be preferred. But, having taught the supreme virtue of hard work, it is difficult to see how the authorities can aim at a paradise in which there will be much leisure and little work. It seems more likely that they will find continually fresh schemes, by which present leisure is to be sacrificed to future productivity. I read recently of an ingenious plan put forward by Russian engineers, for making the White Sea and the northern coasts of Siberia warm, by putting a dam across the Kara Sea. An admirable project, but liable to postpone proletarian comfort for a generation, while the nobility of toil is being displayed amid the ice-fields and snowstorms of the Arctic Ocean. This sort of thing, if it happens, will be the result of regarding the virtue of hard work as an end in itself, rather than as a means to a state of affairs in which it is no longer needed.
+
+The fact is that moving matter about, while a certain amount of it is necessary to our existence, is emphatically not one of the ends of human life. If it were, we should have to consider every navvy superior to Shakespeare. We have been misled in this matter by two causes. One is the necessity of keeping the poor contented, which has led the rich, for thousands of years, to preach the dignity of labor, while taking care themselves to remain undignified in this respect. The other is the new pleasure in mechanism, which makes us delight in the astonishingly clever changes that we can produce on the earth's surface. Neither of these motives makes any great appeal to the actual worker. If you ask him what he thinks the best part of his life, he is not likely to say: 'I enjoy manual work because it makes me feel that I am fulfilling man's noblest task, and because I like to think how much man can transform his planet. It is true that my body demands periods of rest, which I have to fill in as best I may, but I am never so happy as when the morning comes and I can return to the toil from which my contentment springs.' I have never heard working men say this sort of thing. They consider work, as it should be considered, a necessary means to a livelihood, and it is from their leisure that they derive whatever happiness they may enjoy.
+
+It will be said that, while a little leisure is pleasant, men would not know how to fill their days if they had only four hours of work out of the twenty-four. In so far as this is true in the modern world, it is a condemnation of our civilization; it would not have been true at any earlier period. There was formerly a capacity for light-heartedness and play which has been to some extent inhibited by the cult of efficiency. The modern man thinks that everything ought to be done for the sake of something else, and never for its own sake. Serious-minded persons, for example, are continually condemning the habit of going to the cinema, and telling us that it leads the young into crime. But all the work that goes to producing a cinema is respectable, because it is work, and because it brings a money profit. The notion that the desirable activities are those that bring a profit has made everything topsy-turvy. The butcher who provides you with meat and the baker who provides you with bread are praiseworthy, because they are making money; but when you enjoy the food they have provided, you are merely frivolous, unless you eat only to get strength for your work. Broadly speaking, it is held that getting money is good and spending money is bad. Seeing that they are two sides of one transaction, this is absurd; one might as well maintain that keys are good, but keyholes are bad. Whatever merit there may be in the production of goods must be entirely derivative from the advantage to be obtained by consuming them. The individual, in our society, works for profit; but the social purpose of his work lies in the consumption of what he produces. It is this divorce between the individual and the social purpose of production that makes it so difficult for men to think clearly in a world in which profit-making is the incentive to industry. We think too much of production, and too little of consumption. One result is that we attach too little importance to enjoyment and simple happiness, and that we do not judge production by the pleasure that it gives to the consumer.
+
+When I suggest that working hours should be reduced to four, I am not meaning to imply that all the remaining time should necessarily be spent in pure frivolity. I mean that four hours' work a day should entitle a man to the necessities and elementary comforts of life, and that the rest of his time should be his to use as he might see fit. It is an essential part of any such social system that education should be carried further than it usually is at present, and should aim, in part, at providing tastes which would enable a man to use leisure intelligently. I am not thinking mainly of the sort of things that would be considered 'highbrow'. Peasant dances have died out except in remote rural areas, but the impulses which caused them to be cultivated must still exist in human nature. The pleasures of urban populations have become mainly passive: seeing cinemas, watching football matches, listening to the radio, and so on. This results from the fact that their active energies are fully taken up with work; if they had more leisure, they would again enjoy pleasures in which they took an active part.
+
+In the past, there was a small leisure class and a larger working class. The leisure class enjoyed advantages for which there was no basis in social justice; this necessarily made it oppressive, limited its sympathies, and caused it to invent theories by which to justify its privileges. These facts greatly diminished its excellence, but in spite of this drawback it contributed nearly the whole of what we call civilization. It cultivated the arts and discovered the sciences; it wrote the books, invented the philosophies, and refined social relations. Even the liberation of the oppressed has usually been inaugurated from above. Without the leisure class, mankind would never have emerged from barbarism.
+
+The method of a leisure class without duties was, however, extraordinarily wasteful. None of the members of the class had to be taught to be industrious, and the class as a whole was not exceptionally intelligent. The class might produce one Darwin, but against him had to be set tens of thousands of country gentlemen who never thought of anything more intelligent than fox-hunting and punishing poachers. At present, the universities are supposed to provide, in a more systematic way, what the leisure class provided accidentally and as a by-product. This is a great improvement, but it has certain drawbacks. University life is so different from life in the world at large that men who live in academic milieu tend to be unaware of the preoccupations and problems of ordinary men and women; moreover their ways of expressing themselves are usually such as to rob their opinions of the influence that they ought to have upon the general public. Another disadvantage is that in universities studies are organized, and the man who thinks of some original line of research is likely to be discouraged. Academic institutions, therefore, useful as they are, are not adequate guardians of the interests of civilization in a world where everyone outside their walls is too busy for unutilitarian pursuits.
+
+In a world where no one is compelled to work more than four hours a day, every person possessed of scientific curiosity will be able to indulge it, and every painter will be able to paint without starving, however excellent his pictures may be. Young writers will not be obliged to draw attention to themselves by sensational pot-boilers, with a view to acquiring the economic independence needed for monumental works, for which, when the time at last comes, they will have lost the taste and capacity. Men who, in their professional work, have become interested in some phase of economics or government, will be able to develop their ideas without the academic detachment that makes the work of university economists often seem lacking in reality. Medical men will have the time to learn about the progress of medicine, teachers will not be exasperatedly struggling to teach by routine methods things which they learnt in their youth, which may, in the interval, have been proved to be untrue.
+
+Above all, there will be happiness and joy of life, instead of frayed nerves, weariness, and dyspepsia. The work exacted will be enough to make leisure delightful, but not enough to produce exhaustion. Since men will not be tired in their spare time, they will not demand only such amusements as are passive and vapid. At least one per cent will probably devote the time not spent in professional work to pursuits of some public importance, and, since they will not depend upon these pursuits for their livelihood, their originality will be unhampered, and there will be no need to conform to the standards set by elderly pundits. But it is not only in these exceptional cases that the advantages of leisure will appear. Ordinary men and women, having the opportunity of a happy life, will become more kindly and less persecuting and less inclined to view others with suspicion. The taste for war will die out, partly for this reason, and partly because it will involve long and severe work for all. Good nature is, of all moral qualities, the one that the world needs most, and good nature is the result of ease and security, not of a life of arduous struggle. Modern methods of production have given us the possibility of ease and security for all; we have chosen, instead, to have overwork for some and starvation for others. Hitherto we have continued to be as energetic as we were before there were machines; in this we have been foolish, but there is no reason to go on being foolish forever.
+
+[1] Since then, members of the Communist Party have succeeded to this privilege of the warriors and priests.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/wrap.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/wrap.js
new file mode 100644
index 0000000..0cfb76d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/node_modules/wordwrap/test/wrap.js
@@ -0,0 +1,31 @@
+var assert = require('assert');
+var wordwrap = require('wordwrap');
+
+var fs = require('fs');
+var idleness = fs.readFileSync(__dirname + '/idleness.txt', 'utf8');
+
+exports.stop80 = function () {
+    var lines = wordwrap(80)(idleness).split(/\n/);
+    var words = idleness.split(/\s+/);
+    
+    lines.forEach(function (line) {
+        assert.ok(line.length <= 80, 'line > 80 columns');
+        var chunks = line.match(/\S/) ? line.split(/\s+/) : [];
+        assert.deepEqual(chunks, words.splice(0, chunks.length));
+    });
+};
+
+exports.start20stop60 = function () {
+    var lines = wordwrap(20, 100)(idleness).split(/\n/);
+    var words = idleness.split(/\s+/);
+    
+    lines.forEach(function (line) {
+        assert.ok(line.length <= 100, 'line > 100 columns');
+        var chunks = line
+            .split(/\s+/)
+            .filter(function (x) { return x.match(/\S/) })
+        ;
+        assert.deepEqual(chunks, words.splice(0, chunks.length));
+        assert.deepEqual(line.slice(0, 20), new Array(20 + 1).join(' '));
+    });
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/package.json
new file mode 100644
index 0000000..2deab40
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/node_modules/yargs/package.json
@@ -0,0 +1,121 @@
+{
+  "name": "yargs",
+  "version": "3.5.4",
+  "description": "Light-weight option parsing with an argv hash. No optstrings attached.",
+  "main": "./index.js",
+  "files": [
+    "index.js",
+    "lib",
+    "completion.sh.hbs",
+    "LICENSE"
+  ],
+  "dependencies": {
+    "camelcase": "^1.0.2",
+    "decamelize": "^1.0.0",
+    "window-size": "0.1.0",
+    "wordwrap": "0.0.2"
+  },
+  "devDependencies": {
+    "blanket": "^1.1.6",
+    "chai": "^1.10.0",
+    "coveralls": "^2.11.2",
+    "hashish": "0.0.4",
+    "mocha": "2.1.0",
+    "mocha-lcov-reporter": "0.0.1",
+    "mocoverage": "^1.0.0"
+  },
+  "scripts": {
+    "test": "mocha --check-leaks --ui exports --require blanket -R mocoverage"
+  },
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/bcoe/yargs.git"
+  },
+  "config": {
+    "blanket": {
+      "pattern": [
+        "lib",
+        "index.js"
+      ],
+      "data-cover-never": [
+        "node_modules",
+        "test"
+      ],
+      "output-reporter": "spec"
+    }
+  },
+  "keywords": [
+    "argument",
+    "args",
+    "option",
+    "parser",
+    "parsing",
+    "cli",
+    "command"
+  ],
+  "author": {
+    "name": "Alex Ford",
+    "email": "Alex.Ford@CodeTunnel.com",
+    "url": "http://CodeTunnel.com"
+  },
+  "contributors": [
+    {
+      "name": "Benjamin Coe",
+      "email": "ben@npmjs.com",
+      "url": "https://github.com/bcoe"
+    },
+    {
+      "name": "Chris Needham",
+      "email": "chris@chrisneedham.com",
+      "url": "http://chrisneedham.com"
+    },
+    {
+      "name": "James Nylen",
+      "email": "jnylen@gmail.com",
+      "url": "https://github.com/nylen"
+    },
+    {
+      "name": "Benjamin Horsleben",
+      "url": "https://github.com/fizker"
+    }
+  ],
+  "license": "MIT/X11",
+  "engine": {
+    "node": ">=0.4"
+  },
+  "gitHead": "c16cc085501155cf7fd853ccdf8584b05ab92b78",
+  "bugs": {
+    "url": "https://github.com/bcoe/yargs/issues"
+  },
+  "homepage": "https://github.com/bcoe/yargs",
+  "_id": "yargs@3.5.4",
+  "_shasum": "d8aff8f665e94c34bd259bdebd1bfaf0ddd35361",
+  "_from": "yargs@>=3.5.4 <3.6.0",
+  "_npmVersion": "2.7.0",
+  "_nodeVersion": "1.5.0",
+  "_npmUser": {
+    "name": "bcoe",
+    "email": "bencoe@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "chevex",
+      "email": "alex.ford@codetunnel.com"
+    },
+    {
+      "name": "bcoe",
+      "email": "bencoe@gmail.com"
+    },
+    {
+      "name": "nylen",
+      "email": "jnylen@gmail.com"
+    }
+  ],
+  "dist": {
+    "shasum": "d8aff8f665e94c34bd259bdebd1bfaf0ddd35361",
+    "tarball": "http://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/package.json
new file mode 100644
index 0000000..96f1965
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/package.json
@@ -0,0 +1,82 @@
+{
+  "name": "uglify-js",
+  "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
+  "homepage": "http://lisperator.net/uglifyjs",
+  "author": {
+    "name": "Mihai Bazon",
+    "email": "mihai.bazon@gmail.com",
+    "url": "http://lisperator.net/"
+  },
+  "license": "BSD",
+  "version": "2.4.24",
+  "engines": {
+    "node": ">=0.4.0"
+  },
+  "maintainers": [
+    {
+      "name": "caires",
+      "email": "cairesvs@gmail.com"
+    },
+    {
+      "name": "mape",
+      "email": "mape@mape.me"
+    },
+    {
+      "name": "mishoo",
+      "email": "mihai.bazon@gmail.com"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/mishoo/UglifyJS2.git"
+  },
+  "bugs": {
+    "url": "https://github.com/mishoo/UglifyJS2/issues"
+  },
+  "main": "tools/node.js",
+  "bin": {
+    "uglifyjs": "bin/uglifyjs"
+  },
+  "files": [
+    "bin",
+    "lib",
+    "tools",
+    "LICENSE"
+  ],
+  "dependencies": {
+    "async": "~0.2.6",
+    "source-map": "0.1.34",
+    "uglify-to-browserify": "~1.0.0",
+    "yargs": "~3.5.4"
+  },
+  "devDependencies": {
+    "acorn": "~0.6.0",
+    "escodegen": "~1.3.3",
+    "esfuzz": "~0.3.1",
+    "estraverse": "~1.5.1"
+  },
+  "browserify": {
+    "transform": [
+      "uglify-to-browserify"
+    ]
+  },
+  "scripts": {
+    "test": "node test/run-tests.js"
+  },
+  "gitHead": "ba9936a5725f35c3d3452bf3aeba4055de1a1071",
+  "_id": "uglify-js@2.4.24",
+  "_shasum": "fad5755c1e1577658bb06ff9ab6e548c95bebd6e",
+  "_from": "uglify-js@>=2.4.23 <3.0.0",
+  "_npmVersion": "1.4.14",
+  "_npmUser": {
+    "name": "mishoo",
+    "email": "mihai.bazon@gmail.com"
+  },
+  "dist": {
+    "shasum": "fad5755c1e1577658bb06ff9ab6e548c95bebd6e",
+    "tarball": "http://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
+  "readme": "ERROR: No README data found!"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/domprops.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/domprops.json
new file mode 100644
index 0000000..6f6c221
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/domprops.json
@@ -0,0 +1,5603 @@
+{
+    "props": [
+        "$&",
+        "$'",
+        "$*",
+        "$+",
+        "$1",
+        "$2",
+        "$3",
+        "$4",
+        "$5",
+        "$6",
+        "$7",
+        "$8",
+        "$9",
+        "$_",
+        "$`",
+        "$input",
+        "@@iterator",
+        "ABORT_ERR",
+        "ACTIVE",
+        "ACTIVE_ATTRIBUTES",
+        "ACTIVE_TEXTURE",
+        "ACTIVE_UNIFORMS",
+        "ADDITION",
+        "ALIASED_LINE_WIDTH_RANGE",
+        "ALIASED_POINT_SIZE_RANGE",
+        "ALLOW_KEYBOARD_INPUT",
+        "ALLPASS",
+        "ALPHA",
+        "ALPHA_BITS",
+        "ALT_MASK",
+        "ALWAYS",
+        "ANY_TYPE",
+        "ANY_UNORDERED_NODE_TYPE",
+        "ARRAY_BUFFER",
+        "ARRAY_BUFFER_BINDING",
+        "ATTACHED_SHADERS",
+        "ATTRIBUTE_NODE",
+        "AT_TARGET",
+        "AddSearchProvider",
+        "AnalyserNode",
+        "AnimationEvent",
+        "AnonXMLHttpRequest",
+        "ApplicationCache",
+        "ApplicationCacheErrorEvent",
+        "Array",
+        "ArrayBuffer",
+        "Attr",
+        "Audio",
+        "AudioBuffer",
+        "AudioBufferSourceNode",
+        "AudioContext",
+        "AudioDestinationNode",
+        "AudioListener",
+        "AudioNode",
+        "AudioParam",
+        "AudioProcessingEvent",
+        "AudioStreamTrack",
+        "AutocompleteErrorEvent",
+        "BACK",
+        "BAD_BOUNDARYPOINTS_ERR",
+        "BANDPASS",
+        "BLEND",
+        "BLEND_COLOR",
+        "BLEND_DST_ALPHA",
+        "BLEND_DST_RGB",
+        "BLEND_EQUATION",
+        "BLEND_EQUATION_ALPHA",
+        "BLEND_EQUATION_RGB",
+        "BLEND_SRC_ALPHA",
+        "BLEND_SRC_RGB",
+        "BLUE_BITS",
+        "BLUR",
+        "BOOL",
+        "BOOLEAN_TYPE",
+        "BOOL_VEC2",
+        "BOOL_VEC3",
+        "BOOL_VEC4",
+        "BOTH",
+        "BROWSER_DEFAULT_WEBGL",
+        "BUBBLING_PHASE",
+        "BUFFER_SIZE",
+        "BUFFER_USAGE",
+        "BYTE",
+        "BYTES_PER_ELEMENT",
+        "BarProp",
+        "BaseHref",
+        "BatteryManager",
+        "BeforeLoadEvent",
+        "BeforeUnloadEvent",
+        "BiquadFilterNode",
+        "Blob",
+        "BlobEvent",
+        "Boolean",
+        "CAPTURING_PHASE",
+        "CCW",
+        "CDATASection",
+        "CDATA_SECTION_NODE",
+        "CHANGE",
+        "CHARSET_RULE",
+        "CHECKING",
+        "CLAMP_TO_EDGE",
+        "CLICK",
+        "CLOSED",
+        "CLOSING",
+        "COLOR_ATTACHMENT0",
+        "COLOR_BUFFER_BIT",
+        "COLOR_CLEAR_VALUE",
+        "COLOR_WRITEMASK",
+        "COMMENT_NODE",
+        "COMPILE_STATUS",
+        "COMPRESSED_RGBA_S3TC_DXT1_EXT",
+        "COMPRESSED_RGBA_S3TC_DXT3_EXT",
+        "COMPRESSED_RGBA_S3TC_DXT5_EXT",
+        "COMPRESSED_RGB_S3TC_DXT1_EXT",
+        "COMPRESSED_TEXTURE_FORMATS",
+        "CONNECTING",
+        "CONSTANT_ALPHA",
+        "CONSTANT_COLOR",
+        "CONSTRAINT_ERR",
+        "CONTEXT_LOST_WEBGL",
+        "CONTROL_MASK",
+        "COUNTER_STYLE_RULE",
+        "CSS",
+        "CSS2Properties",
+        "CSSCharsetRule",
+        "CSSConditionRule",
+        "CSSCounterStyleRule",
+        "CSSFontFaceRule",
+        "CSSFontFeatureValuesRule",
+        "CSSGroupingRule",
+        "CSSImportRule",
+        "CSSKeyframeRule",
+        "CSSKeyframesRule",
+        "CSSMediaRule",
+        "CSSMozDocumentRule",
+        "CSSNameSpaceRule",
+        "CSSPageRule",
+        "CSSPrimitiveValue",
+        "CSSRule",
+        "CSSRuleList",
+        "CSSStyleDeclaration",
+        "CSSStyleRule",
+        "CSSStyleSheet",
+        "CSSSupportsRule",
+        "CSSUnknownRule",
+        "CSSValue",
+        "CSSValueList",
+        "CSSVariablesDeclaration",
+        "CSSVariablesRule",
+        "CSSViewportRule",
+        "CSS_ATTR",
+        "CSS_CM",
+        "CSS_COUNTER",
+        "CSS_CUSTOM",
+        "CSS_DEG",
+        "CSS_DIMENSION",
+        "CSS_EMS",
+        "CSS_EXS",
+        "CSS_FILTER_BLUR",
+        "CSS_FILTER_BRIGHTNESS",
+        "CSS_FILTER_CONTRAST",
+        "CSS_FILTER_CUSTOM",
+        "CSS_FILTER_DROP_SHADOW",
+        "CSS_FILTER_GRAYSCALE",
+        "CSS_FILTER_HUE_ROTATE",
+        "CSS_FILTER_INVERT",
+        "CSS_FILTER_OPACITY",
+        "CSS_FILTER_REFERENCE",
+        "CSS_FILTER_SATURATE",
+        "CSS_FILTER_SEPIA",
+        "CSS_GRAD",
+        "CSS_HZ",
+        "CSS_IDENT",
+        "CSS_IN",
+        "CSS_INHERIT",
+        "CSS_KHZ",
+        "CSS_MATRIX",
+        "CSS_MATRIX3D",
+        "CSS_MM",
+        "CSS_MS",
+        "CSS_NUMBER",
+        "CSS_PC",
+        "CSS_PERCENTAGE",
+        "CSS_PERSPECTIVE",
+        "CSS_PRIMITIVE_VALUE",
+        "CSS_PT",
+        "CSS_PX",
+        "CSS_RAD",
+        "CSS_RECT",
+        "CSS_RGBCOLOR",
+        "CSS_ROTATE",
+        "CSS_ROTATE3D",
+        "CSS_ROTATEX",
+        "CSS_ROTATEY",
+        "CSS_ROTATEZ",
+        "CSS_S",
+        "CSS_SCALE",
+        "CSS_SCALE3D",
+        "CSS_SCALEX",
+        "CSS_SCALEY",
+        "CSS_SCALEZ",
+        "CSS_SKEW",
+        "CSS_SKEWX",
+        "CSS_SKEWY",
+        "CSS_STRING",
+        "CSS_TRANSLATE",
+        "CSS_TRANSLATE3D",
+        "CSS_TRANSLATEX",
+        "CSS_TRANSLATEY",
+        "CSS_TRANSLATEZ",
+        "CSS_UNKNOWN",
+        "CSS_URI",
+        "CSS_VALUE_LIST",
+        "CSS_VH",
+        "CSS_VMAX",
+        "CSS_VMIN",
+        "CSS_VW",
+        "CULL_FACE",
+        "CULL_FACE_MODE",
+        "CURRENT_PROGRAM",
+        "CURRENT_VERTEX_ATTRIB",
+        "CUSTOM",
+        "CW",
+        "CanvasGradient",
+        "CanvasPattern",
+        "CanvasRenderingContext2D",
+        "CaretPosition",
+        "ChannelMergerNode",
+        "ChannelSplitterNode",
+        "CharacterData",
+        "ClientRect",
+        "ClientRectList",
+        "Clipboard",
+        "ClipboardEvent",
+        "CloseEvent",
+        "Collator",
+        "CommandEvent",
+        "Comment",
+        "CompositionEvent",
+        "Console",
+        "Controllers",
+        "ConvolverNode",
+        "Counter",
+        "Crypto",
+        "CryptoKey",
+        "CustomEvent",
+        "DATABASE_ERR",
+        "DATA_CLONE_ERR",
+        "DATA_ERR",
+        "DBLCLICK",
+        "DECR",
+        "DECR_WRAP",
+        "DELETE_STATUS",
+        "DEPTH_ATTACHMENT",
+        "DEPTH_BITS",
+        "DEPTH_BUFFER_BIT",
+        "DEPTH_CLEAR_VALUE",
+        "DEPTH_COMPONENT",
+        "DEPTH_COMPONENT16",
+        "DEPTH_FUNC",
+        "DEPTH_RANGE",
+        "DEPTH_STENCIL",
+        "DEPTH_STENCIL_ATTACHMENT",
+        "DEPTH_TEST",
+        "DEPTH_WRITEMASK",
+        "DIRECTION_DOWN",
+        "DIRECTION_LEFT",
+        "DIRECTION_RIGHT",
+        "DIRECTION_UP",
+        "DISABLED",
+        "DISPATCH_REQUEST_ERR",
+        "DITHER",
+        "DOCUMENT_FRAGMENT_NODE",
+        "DOCUMENT_NODE",
+        "DOCUMENT_POSITION_CONTAINED_BY",
+        "DOCUMENT_POSITION_CONTAINS",
+        "DOCUMENT_POSITION_DISCONNECTED",
+        "DOCUMENT_POSITION_FOLLOWING",
+        "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC",
+        "DOCUMENT_POSITION_PRECEDING",
+        "DOCUMENT_TYPE_NODE",
+        "DOMCursor",
+        "DOMError",
+        "DOMException",
+        "DOMImplementation",
+        "DOMImplementationLS",
+        "DOMMatrix",
+        "DOMMatrixReadOnly",
+        "DOMParser",
+        "DOMPoint",
+        "DOMPointReadOnly",
+        "DOMQuad",
+        "DOMRect",
+        "DOMRectList",
+        "DOMRectReadOnly",
+        "DOMRequest",
+        "DOMSTRING_SIZE_ERR",
+        "DOMSettableTokenList",
+        "DOMStringList",
+        "DOMStringMap",
+        "DOMTokenList",
+        "DOMTransactionEvent",
+        "DOM_DELTA_LINE",
+        "DOM_DELTA_PAGE",
+        "DOM_DELTA_PIXEL",
+        "DOM_INPUT_METHOD_DROP",
+        "DOM_INPUT_METHOD_HANDWRITING",
+        "DOM_INPUT_METHOD_IME",
+        "DOM_INPUT_METHOD_KEYBOARD",
+        "DOM_INPUT_METHOD_MULTIMODAL",
+        "DOM_INPUT_METHOD_OPTION",
+        "DOM_INPUT_METHOD_PASTE",
+        "DOM_INPUT_METHOD_SCRIPT",
+        "DOM_INPUT_METHOD_UNKNOWN",
+        "DOM_INPUT_METHOD_VOICE",
+        "DOM_KEY_LOCATION_JOYSTICK",
+        "DOM_KEY_LOCATION_LEFT",
+        "DOM_KEY_LOCATION_MOBILE",
+        "DOM_KEY_LOCATION_NUMPAD",
+        "DOM_KEY_LOCATION_RIGHT",
+        "DOM_KEY_LOCATION_STANDARD",
+        "DOM_VK_0",
+        "DOM_VK_1",
+        "DOM_VK_2",
+        "DOM_VK_3",
+        "DOM_VK_4",
+        "DOM_VK_5",
+        "DOM_VK_6",
+        "DOM_VK_7",
+        "DOM_VK_8",
+        "DOM_VK_9",
+        "DOM_VK_A",
+        "DOM_VK_ACCEPT",
+        "DOM_VK_ADD",
+        "DOM_VK_ALT",
+        "DOM_VK_ALTGR",
+        "DOM_VK_AMPERSAND",
+        "DOM_VK_ASTERISK",
+        "DOM_VK_AT",
+        "DOM_VK_ATTN",
+        "DOM_VK_B",
+        "DOM_VK_BACKSPACE",
+        "DOM_VK_BACK_QUOTE",
+        "DOM_VK_BACK_SLASH",
+        "DOM_VK_BACK_SPACE",
+        "DOM_VK_C",
+        "DOM_VK_CANCEL",
+        "DOM_VK_CAPS_LOCK",
+        "DOM_VK_CIRCUMFLEX",
+        "DOM_VK_CLEAR",
+        "DOM_VK_CLOSE_BRACKET",
+        "DOM_VK_CLOSE_CURLY_BRACKET",
+        "DOM_VK_CLOSE_PAREN",
+        "DOM_VK_COLON",
+        "DOM_VK_COMMA",
+        "DOM_VK_CONTEXT_MENU",
+        "DOM_VK_CONTROL",
+        "DOM_VK_CONVERT",
+        "DOM_VK_CRSEL",
+        "DOM_VK_CTRL",
+        "DOM_VK_D",
+        "DOM_VK_DECIMAL",
+        "DOM_VK_DELETE",
+        "DOM_VK_DIVIDE",
+        "DOM_VK_DOLLAR",
+        "DOM_VK_DOUBLE_QUOTE",
+        "DOM_VK_DOWN",
+        "DOM_VK_E",
+        "DOM_VK_EISU",
+        "DOM_VK_END",
+        "DOM_VK_ENTER",
+        "DOM_VK_EQUALS",
+        "DOM_VK_EREOF",
+        "DOM_VK_ESCAPE",
+        "DOM_VK_EXCLAMATION",
+        "DOM_VK_EXECUTE",
+        "DOM_VK_EXSEL",
+        "DOM_VK_F",
+        "DOM_VK_F1",
+        "DOM_VK_F10",
+        "DOM_VK_F11",
+        "DOM_VK_F12",
+        "DOM_VK_F13",
+        "DOM_VK_F14",
+        "DOM_VK_F15",
+        "DOM_VK_F16",
+        "DOM_VK_F17",
+        "DOM_VK_F18",
+        "DOM_VK_F19",
+        "DOM_VK_F2",
+        "DOM_VK_F20",
+        "DOM_VK_F21",
+        "DOM_VK_F22",
+        "DOM_VK_F23",
+        "DOM_VK_F24",
+        "DOM_VK_F25",
+        "DOM_VK_F26",
+        "DOM_VK_F27",
+        "DOM_VK_F28",
+        "DOM_VK_F29",
+        "DOM_VK_F3",
+        "DOM_VK_F30",
+        "DOM_VK_F31",
+        "DOM_VK_F32",
+        "DOM_VK_F33",
+        "DOM_VK_F34",
+        "DOM_VK_F35",
+        "DOM_VK_F36",
+        "DOM_VK_F4",
+        "DOM_VK_F5",
+        "DOM_VK_F6",
+        "DOM_VK_F7",
+        "DOM_VK_F8",
+        "DOM_VK_F9",
+        "DOM_VK_FINAL",
+        "DOM_VK_FRONT",
+        "DOM_VK_G",
+        "DOM_VK_GREATER_THAN",
+        "DOM_VK_H",
+        "DOM_VK_HANGUL",
+        "DOM_VK_HANJA",
+        "DOM_VK_HASH",
+        "DOM_VK_HELP",
+        "DOM_VK_HK_TOGGLE",
+        "DOM_VK_HOME",
+        "DOM_VK_HYPHEN_MINUS",
+        "DOM_VK_I",
+        "DOM_VK_INSERT",
+        "DOM_VK_J",
+        "DOM_VK_JUNJA",
+        "DOM_VK_K",
+        "DOM_VK_KANA",
+        "DOM_VK_KANJI",
+        "DOM_VK_L",
+        "DOM_VK_LEFT",
+        "DOM_VK_LEFT_TAB",
+        "DOM_VK_LESS_THAN",
+        "DOM_VK_M",
+        "DOM_VK_META",
+        "DOM_VK_MODECHANGE",
+        "DOM_VK_MULTIPLY",
+        "DOM_VK_N",
+        "DOM_VK_NONCONVERT",
+        "DOM_VK_NUMPAD0",
+        "DOM_VK_NUMPAD1",
+        "DOM_VK_NUMPAD2",
+        "DOM_VK_NUMPAD3",
+        "DOM_VK_NUMPAD4",
+        "DOM_VK_NUMPAD5",
+        "DOM_VK_NUMPAD6",
+        "DOM_VK_NUMPAD7",
+        "DOM_VK_NUMPAD8",
+        "DOM_VK_NUMPAD9",
+        "DOM_VK_NUM_LOCK",
+        "DOM_VK_O",
+        "DOM_VK_OEM_1",
+        "DOM_VK_OEM_102",
+        "DOM_VK_OEM_2",
+        "DOM_VK_OEM_3",
+        "DOM_VK_OEM_4",
+        "DOM_VK_OEM_5",
+        "DOM_VK_OEM_6",
+        "DOM_VK_OEM_7",
+        "DOM_VK_OEM_8",
+        "DOM_VK_OEM_COMMA",
+        "DOM_VK_OEM_MINUS",
+        "DOM_VK_OEM_PERIOD",
+        "DOM_VK_OEM_PLUS",
+        "DOM_VK_OPEN_BRACKET",
+        "DOM_VK_OPEN_CURLY_BRACKET",
+        "DOM_VK_OPEN_PAREN",
+        "DOM_VK_P",
+        "DOM_VK_PA1",
+        "DOM_VK_PAGEDOWN",
+        "DOM_VK_PAGEUP",
+        "DOM_VK_PAGE_DOWN",
+        "DOM_VK_PAGE_UP",
+        "DOM_VK_PAUSE",
+        "DOM_VK_PERCENT",
+        "DOM_VK_PERIOD",
+        "DOM_VK_PIPE",
+        "DOM_VK_PLAY",
+        "DOM_VK_PLUS",
+        "DOM_VK_PRINT",
+        "DOM_VK_PRINTSCREEN",
+        "DOM_VK_PROCESSKEY",
+        "DOM_VK_PROPERITES",
+        "DOM_VK_Q",
+        "DOM_VK_QUESTION_MARK",
+        "DOM_VK_QUOTE",
+        "DOM_VK_R",
+        "DOM_VK_REDO",
+        "DOM_VK_RETURN",
+        "DOM_VK_RIGHT",
+        "DOM_VK_S",
+        "DOM_VK_SCROLL_LOCK",
+        "DOM_VK_SELECT",
+        "DOM_VK_SEMICOLON",
+        "DOM_VK_SEPARATOR",
+        "DOM_VK_SHIFT",
+        "DOM_VK_SLASH",
+        "DOM_VK_SLEEP",
+        "DOM_VK_SPACE",
+        "DOM_VK_SUBTRACT",
+        "DOM_VK_T",
+        "DOM_VK_TAB",
+        "DOM_VK_TILDE",
+        "DOM_VK_U",
+        "DOM_VK_UNDERSCORE",
+        "DOM_VK_UNDO",
+        "DOM_VK_UNICODE",
+        "DOM_VK_UP",
+        "DOM_VK_V",
+        "DOM_VK_VOLUME_DOWN",
+        "DOM_VK_VOLUME_MUTE",
+        "DOM_VK_VOLUME_UP",
+        "DOM_VK_W",
+        "DOM_VK_WIN",
+        "DOM_VK_WINDOW",
+        "DOM_VK_WIN_ICO_00",
+        "DOM_VK_WIN_ICO_CLEAR",
+        "DOM_VK_WIN_ICO_HELP",
+        "DOM_VK_WIN_OEM_ATTN",
+        "DOM_VK_WIN_OEM_AUTO",
+        "DOM_VK_WIN_OEM_BACKTAB",
+        "DOM_VK_WIN_OEM_CLEAR",
+        "DOM_VK_WIN_OEM_COPY",
+        "DOM_VK_WIN_OEM_CUSEL",
+        "DOM_VK_WIN_OEM_ENLW",
+        "DOM_VK_WIN_OEM_FINISH",
+        "DOM_VK_WIN_OEM_FJ_JISHO",
+        "DOM_VK_WIN_OEM_FJ_LOYA",
+        "DOM_VK_WIN_OEM_FJ_MASSHOU",
+        "DOM_VK_WIN_OEM_FJ_ROYA",
+        "DOM_VK_WIN_OEM_FJ_TOUROKU",
+        "DOM_VK_WIN_OEM_JUMP",
+        "DOM_VK_WIN_OEM_PA1",
+        "DOM_VK_WIN_OEM_PA2",
+        "DOM_VK_WIN_OEM_PA3",
+        "DOM_VK_WIN_OEM_RESET",
+        "DOM_VK_WIN_OEM_WSCTRL",
+        "DOM_VK_X",
+        "DOM_VK_XF86XK_ADD_FAVORITE",
+        "DOM_VK_XF86XK_APPLICATION_LEFT",
+        "DOM_VK_XF86XK_APPLICATION_RIGHT",
+        "DOM_VK_XF86XK_AUDIO_CYCLE_TRACK",
+        "DOM_VK_XF86XK_AUDIO_FORWARD",
+        "DOM_VK_XF86XK_AUDIO_LOWER_VOLUME",
+        "DOM_VK_XF86XK_AUDIO_MEDIA",
+        "DOM_VK_XF86XK_AUDIO_MUTE",
+        "DOM_VK_XF86XK_AUDIO_NEXT",
+        "DOM_VK_XF86XK_AUDIO_PAUSE",
+        "DOM_VK_XF86XK_AUDIO_PLAY",
+        "DOM_VK_XF86XK_AUDIO_PREV",
+        "DOM_VK_XF86XK_AUDIO_RAISE_VOLUME",
+        "DOM_VK_XF86XK_AUDIO_RANDOM_PLAY",
+        "DOM_VK_XF86XK_AUDIO_RECORD",
+        "DOM_VK_XF86XK_AUDIO_REPEAT",
+        "DOM_VK_XF86XK_AUDIO_REWIND",
+        "DOM_VK_XF86XK_AUDIO_STOP",
+        "DOM_VK_XF86XK_AWAY",
+        "DOM_VK_XF86XK_BACK",
+        "DOM_VK_XF86XK_BACK_FORWARD",
+        "DOM_VK_XF86XK_BATTERY",
+        "DOM_VK_XF86XK_BLUE",
+        "DOM_VK_XF86XK_BLUETOOTH",
+        "DOM_VK_XF86XK_BOOK",
+        "DOM_VK_XF86XK_BRIGHTNESS_ADJUST",
+        "DOM_VK_XF86XK_CALCULATOR",
+        "DOM_VK_XF86XK_CALENDAR",
+        "DOM_VK_XF86XK_CD",
+        "DOM_VK_XF86XK_CLOSE",
+        "DOM_VK_XF86XK_COMMUNITY",
+        "DOM_VK_XF86XK_CONTRAST_ADJUST",
+        "DOM_VK_XF86XK_COPY",
+        "DOM_VK_XF86XK_CUT",
+        "DOM_VK_XF86XK_CYCLE_ANGLE",
+        "DOM_VK_XF86XK_DISPLAY",
+        "DOM_VK_XF86XK_DOCUMENTS",
+        "DOM_VK_XF86XK_DOS",
+        "DOM_VK_XF86XK_EJECT",
+        "DOM_VK_XF86XK_EXCEL",
+        "DOM_VK_XF86XK_EXPLORER",
+        "DOM_VK_XF86XK_FAVORITES",
+        "DOM_VK_XF86XK_FINANCE",
+        "DOM_VK_XF86XK_FORWARD",
+        "DOM_VK_XF86XK_FRAME_BACK",
+        "DOM_VK_XF86XK_FRAME_FORWARD",
+        "DOM_VK_XF86XK_GAME",
+        "DOM_VK_XF86XK_GO",
+        "DOM_VK_XF86XK_GREEN",
+        "DOM_VK_XF86XK_HIBERNATE",
+        "DOM_VK_XF86XK_HISTORY",
+        "DOM_VK_XF86XK_HOME_PAGE",
+        "DOM_VK_XF86XK_HOT_LINKS",
+        "DOM_VK_XF86XK_I_TOUCH",
+        "DOM_VK_XF86XK_KBD_BRIGHTNESS_DOWN",
+        "DOM_VK_XF86XK_KBD_BRIGHTNESS_UP",
+        "DOM_VK_XF86XK_KBD_LIGHT_ON_OFF",
+        "DOM_VK_XF86XK_LAUNCH0",
+        "DOM_VK_XF86XK_LAUNCH1",
+        "DOM_VK_XF86XK_LAUNCH2",
+        "DOM_VK_XF86XK_LAUNCH3",
+        "DOM_VK_XF86XK_LAUNCH4",
+        "DOM_VK_XF86XK_LAUNCH5",
+        "DOM_VK_XF86XK_LAUNCH6",
+        "DOM_VK_XF86XK_LAUNCH7",
+        "DOM_VK_XF86XK_LAUNCH8",
+        "DOM_VK_XF86XK_LAUNCH9",
+        "DOM_VK_XF86XK_LAUNCH_A",
+        "DOM_VK_XF86XK_LAUNCH_B",
+        "DOM_VK_XF86XK_LAUNCH_C",
+        "DOM_VK_XF86XK_LAUNCH_D",
+        "DOM_VK_XF86XK_LAUNCH_E",
+        "DOM_VK_XF86XK_LAUNCH_F",
+        "DOM_VK_XF86XK_LIGHT_BULB",
+        "DOM_VK_XF86XK_LOG_OFF",
+        "DOM_VK_XF86XK_MAIL",
+        "DOM_VK_XF86XK_MAIL_FORWARD",
+        "DOM_VK_XF86XK_MARKET",
+        "DOM_VK_XF86XK_MEETING",
+        "DOM_VK_XF86XK_MEMO",
+        "DOM_VK_XF86XK_MENU_KB",
+        "DOM_VK_XF86XK_MENU_PB",
+        "DOM_VK_XF86XK_MESSENGER",
+        "DOM_VK_XF86XK_MON_BRIGHTNESS_DOWN",
+        "DOM_VK_XF86XK_MON_BRIGHTNESS_UP",
+        "DOM_VK_XF86XK_MUSIC",
+        "DOM_VK_XF86XK_MY_COMPUTER",
+        "DOM_VK_XF86XK_MY_SITES",
+        "DOM_VK_XF86XK_NEW",
+        "DOM_VK_XF86XK_NEWS",
+        "DOM_VK_XF86XK_OFFICE_HOME",
+        "DOM_VK_XF86XK_OPEN",
+        "DOM_VK_XF86XK_OPEN_URL",
+        "DOM_VK_XF86XK_OPTION",
+        "DOM_VK_XF86XK_PASTE",
+        "DOM_VK_XF86XK_PHONE",
+        "DOM_VK_XF86XK_PICTURES",
+        "DOM_VK_XF86XK_POWER_DOWN",
+        "DOM_VK_XF86XK_POWER_OFF",
+        "DOM_VK_XF86XK_RED",
+        "DOM_VK_XF86XK_REFRESH",
+        "DOM_VK_XF86XK_RELOAD",
+        "DOM_VK_XF86XK_REPLY",
+        "DOM_VK_XF86XK_ROCKER_DOWN",
+        "DOM_VK_XF86XK_ROCKER_ENTER",
+        "DOM_VK_XF86XK_ROCKER_UP",
+        "DOM_VK_XF86XK_ROTATE_WINDOWS",
+        "DOM_VK_XF86XK_ROTATION_KB",
+        "DOM_VK_XF86XK_ROTATION_PB",
+        "DOM_VK_XF86XK_SAVE",
+        "DOM_VK_XF86XK_SCREEN_SAVER",
+        "DOM_VK_XF86XK_SCROLL_CLICK",
+        "DOM_VK_XF86XK_SCROLL_DOWN",
+        "DOM_VK_XF86XK_SCROLL_UP",
+        "DOM_VK_XF86XK_SEARCH",
+        "DOM_VK_XF86XK_SEND",
+        "DOM_VK_XF86XK_SHOP",
+        "DOM_VK_XF86XK_SPELL",
+        "DOM_VK_XF86XK_SPLIT_SCREEN",
+        "DOM_VK_XF86XK_STANDBY",
+        "DOM_VK_XF86XK_START",
+        "DOM_VK_XF86XK_STOP",
+        "DOM_VK_XF86XK_SUBTITLE",
+        "DOM_VK_XF86XK_SUPPORT",
+        "DOM_VK_XF86XK_SUSPEND",
+        "DOM_VK_XF86XK_TASK_PANE",
+        "DOM_VK_XF86XK_TERMINAL",
+        "DOM_VK_XF86XK_TIME",
+        "DOM_VK_XF86XK_TOOLS",
+        "DOM_VK_XF86XK_TOP_MENU",
+        "DOM_VK_XF86XK_TO_DO_LIST",
+        "DOM_VK_XF86XK_TRAVEL",
+        "DOM_VK_XF86XK_USER1KB",
+        "DOM_VK_XF86XK_USER2KB",
+        "DOM_VK_XF86XK_USER_PB",
+        "DOM_VK_XF86XK_UWB",
+        "DOM_VK_XF86XK_VENDOR_HOME",
+        "DOM_VK_XF86XK_VIDEO",
+        "DOM_VK_XF86XK_VIEW",
+        "DOM_VK_XF86XK_WAKE_UP",
+        "DOM_VK_XF86XK_WEB_CAM",
+        "DOM_VK_XF86XK_WHEEL_BUTTON",
+        "DOM_VK_XF86XK_WLAN",
+        "DOM_VK_XF86XK_WORD",
+        "DOM_VK_XF86XK_WWW",
+        "DOM_VK_XF86XK_XFER",
+        "DOM_VK_XF86XK_YELLOW",
+        "DOM_VK_XF86XK_ZOOM_IN",
+        "DOM_VK_XF86XK_ZOOM_OUT",
+        "DOM_VK_Y",
+        "DOM_VK_Z",
+        "DOM_VK_ZOOM",
+        "DONE",
+        "DONT_CARE",
+        "DOWNLOADING",
+        "DRAGDROP",
+        "DST_ALPHA",
+        "DST_COLOR",
+        "DYNAMIC_DRAW",
+        "DataChannel",
+        "DataTransfer",
+        "DataTransferItem",
+        "DataTransferItemList",
+        "DataView",
+        "Date",
+        "DateTimeFormat",
+        "DelayNode",
+        "DesktopNotification",
+        "DesktopNotificationCenter",
+        "DeviceLightEvent",
+        "DeviceMotionEvent",
+        "DeviceOrientationEvent",
+        "DeviceProximityEvent",
+        "DeviceStorage",
+        "DeviceStorageChangeEvent",
+        "Document",
+        "DocumentFragment",
+        "DocumentType",
+        "DragEvent",
+        "DynamicsCompressorNode",
+        "E",
+        "ELEMENT_ARRAY_BUFFER",
+        "ELEMENT_ARRAY_BUFFER_BINDING",
+        "ELEMENT_NODE",
+        "EMPTY",
+        "ENCODING_ERR",
+        "ENDED",
+        "END_TO_END",
+        "END_TO_START",
+        "ENTITY_NODE",
+        "ENTITY_REFERENCE_NODE",
+        "EPSILON",
+        "EQUAL",
+        "EQUALPOWER",
+        "ERROR",
+        "EXPONENTIAL_DISTANCE",
+        "Element",
+        "ElementQuery",
+        "Entity",
+        "EntityReference",
+        "Error",
+        "ErrorEvent",
+        "EvalError",
+        "Event",
+        "EventException",
+        "EventSource",
+        "EventTarget",
+        "External",
+        "FASTEST",
+        "FIDOSDK",
+        "FILTER_ACCEPT",
+        "FILTER_INTERRUPT",
+        "FILTER_REJECT",
+        "FILTER_SKIP",
+        "FINISHED_STATE",
+        "FIRST_ORDERED_NODE_TYPE",
+        "FLOAT",
+        "FLOAT_MAT2",
+        "FLOAT_MAT3",
+        "FLOAT_MAT4",
+        "FLOAT_VEC2",
+        "FLOAT_VEC3",
+        "FLOAT_VEC4",
+        "FOCUS",
+        "FONT_FACE_RULE",
+        "FONT_FEATURE_VALUES_RULE",
+        "FRAGMENT_SHADER",
+        "FRAGMENT_SHADER_DERIVATIVE_HINT_OES",
+        "FRAMEBUFFER",
+        "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME",
+        "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
+        "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE",
+        "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL",
+        "FRAMEBUFFER_BINDING",
+        "FRAMEBUFFER_COMPLETE",
+        "FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
+        "FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
+        "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
+        "FRAMEBUFFER_UNSUPPORTED",
+        "FRONT",
+        "FRONT_AND_BACK",
+        "FRONT_FACE",
+        "FUNC_ADD",
+        "FUNC_REVERSE_SUBTRACT",
+        "FUNC_SUBTRACT",
+        "Feed",
+        "FeedEntry",
+        "File",
+        "FileError",
+        "FileList",
+        "FileReader",
+        "FindInPage",
+        "Float32Array",
+        "Float64Array",
+        "FocusEvent",
+        "FontFace",
+        "FormData",
+        "Function",
+        "GENERATE_MIPMAP_HINT",
+        "GEQUAL",
+        "GREATER",
+        "GREEN_BITS",
+        "GainNode",
+        "Gamepad",
+        "GamepadButton",
+        "GamepadEvent",
+        "GestureEvent",
+        "HAVE_CURRENT_DATA",
+        "HAVE_ENOUGH_DATA",
+        "HAVE_FUTURE_DATA",
+        "HAVE_METADATA",
+        "HAVE_NOTHING",
+        "HEADERS_RECEIVED",
+        "HIDDEN",
+        "HIERARCHY_REQUEST_ERR",
+        "HIGHPASS",
+        "HIGHSHELF",
+        "HIGH_FLOAT",
+        "HIGH_INT",
+        "HORIZONTAL",
+        "HORIZONTAL_AXIS",
+        "HRTF",
+        "HTMLAllCollection",
+        "HTMLAnchorElement",
+        "HTMLAppletElement",
+        "HTMLAreaElement",
+        "HTMLAudioElement",
+        "HTMLBRElement",
+        "HTMLBaseElement",
+        "HTMLBaseFontElement",
+        "HTMLBlockquoteElement",
+        "HTMLBodyElement",
+        "HTMLButtonElement",
+        "HTMLCanvasElement",
+        "HTMLCollection",
+        "HTMLCommandElement",
+        "HTMLContentElement",
+        "HTMLDListElement",
+        "HTMLDataElement",
+        "HTMLDataListElement",
+        "HTMLDetailsElement",
+        "HTMLDialogElement",
+        "HTMLDirectoryElement",
+        "HTMLDivElement",
+        "HTMLDocument",
+        "HTMLElement",
+        "HTMLEmbedElement",
+        "HTMLFieldSetElement",
+        "HTMLFontElement",
+        "HTMLFormControlsCollection",
+        "HTMLFormElement",
+        "HTMLFrameElement",
+        "HTMLFrameSetElement",
+        "HTMLHRElement",
+        "HTMLHeadElement",
+        "HTMLHeadingElement",
+        "HTMLHtmlElement",
+        "HTMLIFrameElement",
+        "HTMLImageElement",
+        "HTMLInputElement",
+        "HTMLIsIndexElement",
+        "HTMLKeygenElement",
+        "HTMLLIElement",
+        "HTMLLabelElement",
+        "HTMLLegendElement",
+        "HTMLLinkElement",
+        "HTMLMapElement",
+        "HTMLMarqueeElement",
+        "HTMLMediaElement",
+        "HTMLMenuElement",
+        "HTMLMenuItemElement",
+        "HTMLMetaElement",
+        "HTMLMeterElement",
+        "HTMLModElement",
+        "HTMLOListElement",
+        "HTMLObjectElement",
+        "HTMLOptGroupElement",
+        "HTMLOptionElement",
+        "HTMLOptionsCollection",
+        "HTMLOutputElement",
+        "HTMLParagraphElement",
+        "HTMLParamElement",
+        "HTMLPictureElement",
+        "HTMLPreElement",
+        "HTMLProgressElement",
+        "HTMLPropertiesCollection",
+        "HTMLQuoteElement",
+        "HTMLScriptElement",
+        "HTMLSelectElement",
+        "HTMLShadowElement",
+        "HTMLSourceElement",
+        "HTMLSpanElement",
+        "HTMLStyleElement",
+        "HTMLTableCaptionElement",
+        "HTMLTableCellElement",
+        "HTMLTableColElement",
+        "HTMLTableElement",
+        "HTMLTableRowElement",
+        "HTMLTableSectionElement",
+        "HTMLTemplateElement",
+        "HTMLTextAreaElement",
+        "HTMLTimeElement",
+        "HTMLTitleElement",
+        "HTMLTrackElement",
+        "HTMLUListElement",
+        "HTMLUnknownElement",
+        "HTMLVideoElement",
+        "HashChangeEvent",
+        "Headers",
+        "History",
+        "ICE_CHECKING",
+        "ICE_CLOSED",
+        "ICE_COMPLETED",
+        "ICE_CONNECTED",
+        "ICE_FAILED",
+        "ICE_GATHERING",
+        "ICE_WAITING",
+        "IDBCursor",
+        "IDBCursorWithValue",
+        "IDBDatabase",
+        "IDBDatabaseException",
+        "IDBFactory",
+        "IDBFileHandle",
+        "IDBFileRequest",
+        "IDBIndex",
+        "IDBKeyRange",
+        "IDBMutableFile",
+        "IDBObjectStore",
+        "IDBOpenDBRequest",
+        "IDBRequest",
+        "IDBTransaction",
+        "IDBVersionChangeEvent",
+        "IDLE",
+        "IMPLEMENTATION_COLOR_READ_FORMAT",
+        "IMPLEMENTATION_COLOR_READ_TYPE",
+        "IMPORT_RULE",
+        "INCR",
+        "INCR_WRAP",
+        "INDEX_SIZE_ERR",
+        "INT",
+        "INT_VEC2",
+        "INT_VEC3",
+        "INT_VEC4",
+        "INUSE_ATTRIBUTE_ERR",
+        "INVALID_ACCESS_ERR",
+        "INVALID_CHARACTER_ERR",
+        "INVALID_ENUM",
+        "INVALID_EXPRESSION_ERR",
+        "INVALID_FRAMEBUFFER_OPERATION",
+        "INVALID_MODIFICATION_ERR",
+        "INVALID_NODE_TYPE_ERR",
+        "INVALID_OPERATION",
+        "INVALID_STATE_ERR",
+        "INVALID_VALUE",
+        "INVERSE_DISTANCE",
+        "INVERT",
+        "IceCandidate",
+        "Image",
+        "ImageBitmap",
+        "ImageData",
+        "Infinity",
+        "InputEvent",
+        "InputMethodContext",
+        "InstallTrigger",
+        "Int16Array",
+        "Int32Array",
+        "Int8Array",
+        "Intent",
+        "InternalError",
+        "Intl",
+        "IsSearchProviderInstalled",
+        "Iterator",
+        "JSON",
+        "KEEP",
+        "KEYDOWN",
+        "KEYFRAMES_RULE",
+        "KEYFRAME_RULE",
+        "KEYPRESS",
+        "KEYUP",
+        "KeyEvent",
+        "KeyboardEvent",
+        "LENGTHADJUST_SPACING",
+        "LENGTHADJUST_SPACINGANDGLYPHS",
+        "LENGTHADJUST_UNKNOWN",
+        "LEQUAL",
+        "LESS",
+        "LINEAR",
+        "LINEAR_DISTANCE",
+        "LINEAR_MIPMAP_LINEAR",
+        "LINEAR_MIPMAP_NEAREST",
+        "LINES",
+        "LINE_LOOP",
+        "LINE_STRIP",
+        "LINE_WIDTH",
+        "LINK_STATUS",
+        "LIVE",
+        "LN10",
+        "LN2",
+        "LOADED",
+        "LOADING",
+        "LOG10E",
+        "LOG2E",
+        "LOWPASS",
+        "LOWSHELF",
+        "LOW_FLOAT",
+        "LOW_INT",
+        "LSException",
+        "LSParserFilter",
+        "LUMINANCE",
+        "LUMINANCE_ALPHA",
+        "LocalMediaStream",
+        "Location",
+        "MAX_COMBINED_TEXTURE_IMAGE_UNITS",
+        "MAX_CUBE_MAP_TEXTURE_SIZE",
+        "MAX_FRAGMENT_UNIFORM_VECTORS",
+        "MAX_RENDERBUFFER_SIZE",
+        "MAX_SAFE_INTEGER",
+        "MAX_TEXTURE_IMAGE_UNITS",
+        "MAX_TEXTURE_MAX_ANISOTROPY_EXT",
+        "MAX_TEXTURE_SIZE",
+        "MAX_VALUE",
+        "MAX_VARYING_VECTORS",
+        "MAX_VERTEX_ATTRIBS",
+        "MAX_VERTEX_TEXTURE_IMAGE_UNITS",
+        "MAX_VERTEX_UNIFORM_VECTORS",
+        "MAX_VIEWPORT_DIMS",
+        "MEDIA_ERR_ABORTED",
+        "MEDIA_ERR_DECODE",
+        "MEDIA_ERR_ENCRYPTED",
+        "MEDIA_ERR_NETWORK",
+        "MEDIA_ERR_SRC_NOT_SUPPORTED",
+        "MEDIA_KEYERR_CLIENT",
+        "MEDIA_KEYERR_DOMAIN",
+        "MEDIA_KEYERR_HARDWARECHANGE",
+        "MEDIA_KEYERR_OUTPUT",
+        "MEDIA_KEYERR_SERVICE",
+        "MEDIA_KEYERR_UNKNOWN",
+        "MEDIA_RULE",
+        "MEDIUM_FLOAT",
+        "MEDIUM_INT",
+        "META_MASK",
+        "MIN_SAFE_INTEGER",
+        "MIN_VALUE",
+        "MIRRORED_REPEAT",
+        "MODE_ASYNCHRONOUS",
+        "MODE_SYNCHRONOUS",
+        "MODIFICATION",
+        "MOUSEDOWN",
+        "MOUSEDRAG",
+        "MOUSEMOVE",
+        "MOUSEOUT",
+        "MOUSEOVER",
+        "MOUSEUP",
+        "MOZ_KEYFRAMES_RULE",
+        "MOZ_KEYFRAME_RULE",
+        "MOZ_SOURCE_CURSOR",
+        "MOZ_SOURCE_ERASER",
+        "MOZ_SOURCE_KEYBOARD",
+        "MOZ_SOURCE_MOUSE",
+        "MOZ_SOURCE_PEN",
+        "MOZ_SOURCE_TOUCH",
+        "MOZ_SOURCE_UNKNOWN",
+        "MSGESTURE_FLAG_BEGIN",
+        "MSGESTURE_FLAG_CANCEL",
+        "MSGESTURE_FLAG_END",
+        "MSGESTURE_FLAG_INERTIA",
+        "MSGESTURE_FLAG_NONE",
+        "MSPOINTER_TYPE_MOUSE",
+        "MSPOINTER_TYPE_PEN",
+        "MSPOINTER_TYPE_TOUCH",
+        "MS_ASYNC_CALLBACK_STATUS_ASSIGN_DELEGATE",
+        "MS_ASYNC_CALLBACK_STATUS_CANCEL",
+        "MS_ASYNC_CALLBACK_STATUS_CHOOSEANY",
+        "MS_ASYNC_CALLBACK_STATUS_ERROR",
+        "MS_ASYNC_CALLBACK_STATUS_JOIN",
+        "MS_ASYNC_OP_STATUS_CANCELED",
+        "MS_ASYNC_OP_STATUS_ERROR",
+        "MS_ASYNC_OP_STATUS_SUCCESS",
+        "MS_MANIPULATION_STATE_ACTIVE",
+        "MS_MANIPULATION_STATE_CANCELLED",
+        "MS_MANIPULATION_STATE_COMMITTED",
+        "MS_MANIPULATION_STATE_DRAGGING",
+        "MS_MANIPULATION_STATE_INERTIA",
+        "MS_MANIPULATION_STATE_PRESELECT",
+        "MS_MANIPULATION_STATE_SELECTING",
+        "MS_MANIPULATION_STATE_STOPPED",
+        "MS_MEDIA_ERR_ENCRYPTED",
+        "MS_MEDIA_KEYERR_CLIENT",
+        "MS_MEDIA_KEYERR_DOMAIN",
+        "MS_MEDIA_KEYERR_HARDWARECHANGE",
+        "MS_MEDIA_KEYERR_OUTPUT",
+        "MS_MEDIA_KEYERR_SERVICE",
+        "MS_MEDIA_KEYERR_UNKNOWN",
+        "Map",
+        "Math",
+        "MediaController",
+        "MediaDevices",
+        "MediaElementAudioSourceNode",
+        "MediaEncryptedEvent",
+        "MediaError",
+        "MediaKeyError",
+        "MediaKeyEvent",
+        "MediaKeyMessageEvent",
+        "MediaKeyNeededEvent",
+        "MediaKeySession",
+        "MediaKeyStatusMap",
+        "MediaKeySystemAccess",
+        "MediaKeys",
+        "MediaList",
+        "MediaQueryList",
+        "MediaQueryListEvent",
+        "MediaRecorder",
+        "MediaSource",
+        "MediaStream",
+        "MediaStreamAudioDestinationNode",
+        "MediaStreamAudioSourceNode",
+        "MediaStreamEvent",
+        "MediaStreamTrack",
+        "MediaStreamTrackEvent",
+        "MessageChannel",
+        "MessageEvent",
+        "MessagePort",
+        "Methods",
+        "MimeType",
+        "MimeTypeArray",
+        "MouseEvent",
+        "MouseScrollEvent",
+        "MozAnimation",
+        "MozAnimationDelay",
+        "MozAnimationDirection",
+        "MozAnimationDuration",
+        "MozAnimationFillMode",
+        "MozAnimationIterationCount",
+        "MozAnimationName",
+        "MozAnimationPlayState",
+        "MozAnimationTimingFunction",
+        "MozAppearance",
+        "MozBackfaceVisibility",
+        "MozBinding",
+        "MozBorderBottomColors",
+        "MozBorderEnd",
+        "MozBorderEndColor",
+        "MozBorderEndStyle",
+        "MozBorderEndWidth",
+        "MozBorderImage",
+        "MozBorderLeftColors",
+        "MozBorderRightColors",
+        "MozBorderStart",
+        "MozBorderStartColor",
+        "MozBorderStartStyle",
+        "MozBorderStartWidth",
+        "MozBorderTopColors",
+        "MozBoxAlign",
+        "MozBoxDirection",
+        "MozBoxFlex",
+        "MozBoxOrdinalGroup",
+        "MozBoxOrient",
+        "MozBoxPack",
+        "MozBoxSizing",
+        "MozCSSKeyframeRule",
+        "MozCSSKeyframesRule",
+        "MozColumnCount",
+        "MozColumnFill",
+        "MozColumnGap",
+        "MozColumnRule",
+        "MozColumnRuleColor",
+        "MozColumnRuleStyle",
+        "MozColumnRuleWidth",
+        "MozColumnWidth",
+        "MozColumns",
+        "MozContactChangeEvent",
+        "MozFloatEdge",
+        "MozFontFeatureSettings",
+        "MozFontLanguageOverride",
+        "MozForceBrokenImageIcon",
+        "MozHyphens",
+        "MozImageRegion",
+        "MozMarginEnd",
+        "MozMarginStart",
+        "MozMmsEvent",
+        "MozMmsMessage",
+        "MozMobileMessageThread",
+        "MozOSXFontSmoothing",
+        "MozOrient",
+        "MozOutlineRadius",
+        "MozOutlineRadiusBottomleft",
+        "MozOutlineRadiusBottomright",
+        "MozOutlineRadiusTopleft",
+        "MozOutlineRadiusTopright",
+        "MozPaddingEnd",
+        "MozPaddingStart",
+        "MozPerspective",
+        "MozPerspectiveOrigin",
+        "MozPowerManager",
+        "MozSettingsEvent",
+        "MozSmsEvent",
+        "MozSmsMessage",
+        "MozStackSizing",
+        "MozTabSize",
+        "MozTextAlignLast",
+        "MozTextDecorationColor",
+        "MozTextDecorationLine",
+        "MozTextDecorationStyle",
+        "MozTextSizeAdjust",
+        "MozTransform",
+        "MozTransformOrigin",
+        "MozTransformStyle",
+        "MozTransition",
+        "MozTransitionDelay",
+        "MozTransitionDuration",
+        "MozTransitionProperty",
+        "MozTransitionTimingFunction",
+        "MozUserFocus",
+        "MozUserInput",
+        "MozUserModify",
+        "MozUserSelect",
+        "MozWindowDragging",
+        "MozWindowShadow",
+        "MutationEvent",
+        "MutationObserver",
+        "MutationRecord",
+        "NAMESPACE_ERR",
+        "NAMESPACE_RULE",
+        "NEAREST",
+        "NEAREST_MIPMAP_LINEAR",
+        "NEAREST_MIPMAP_NEAREST",
+        "NEGATIVE_INFINITY",
+        "NETWORK_EMPTY",
+        "NETWORK_ERR",
+        "NETWORK_IDLE",
+        "NETWORK_LOADED",
+        "NETWORK_LOADING",
+        "NETWORK_NO_SOURCE",
+        "NEVER",
+        "NEW",
+        "NEXT",
+        "NEXT_NO_DUPLICATE",
+        "NICEST",
+        "NODE_AFTER",
+        "NODE_BEFORE",
+        "NODE_BEFORE_AND_AFTER",
+        "NODE_INSIDE",
+        "NONE",
+        "NON_TRANSIENT_ERR",
+        "NOTATION_NODE",
+        "NOTCH",
+        "NOTEQUAL",
+        "NOT_ALLOWED_ERR",
+        "NOT_FOUND_ERR",
+        "NOT_READABLE_ERR",
+        "NOT_SUPPORTED_ERR",
+        "NO_DATA_ALLOWED_ERR",
+        "NO_ERR",
+        "NO_ERROR",
+        "NO_MODIFICATION_ALLOWED_ERR",
+        "NUMBER_TYPE",
+        "NUM_COMPRESSED_TEXTURE_FORMATS",
+        "NaN",
+        "NamedNodeMap",
+        "Navigator",
+        "NearbyLinks",
+        "NetworkInformation",
+        "Node",
+        "NodeFilter",
+        "NodeIterator",
+        "NodeList",
+        "Notation",
+        "Notification",
+        "NotifyPaintEvent",
+        "Number",
+        "NumberFormat",
+        "OBSOLETE",
+        "ONE",
+        "ONE_MINUS_CONSTANT_ALPHA",
+        "ONE_MINUS_CONSTANT_COLOR",
+        "ONE_MINUS_DST_ALPHA",
+        "ONE_MINUS_DST_COLOR",
+        "ONE_MINUS_SRC_ALPHA",
+        "ONE_MINUS_SRC_COLOR",
+        "OPEN",
+        "OPENED",
+        "OPENING",
+        "ORDERED_NODE_ITERATOR_TYPE",
+        "ORDERED_NODE_SNAPSHOT_TYPE",
+        "OUT_OF_MEMORY",
+        "Object",
+        "OfflineAudioCompletionEvent",
+        "OfflineAudioContext",
+        "OfflineResourceList",
+        "Option",
+        "OscillatorNode",
+        "OverflowEvent",
+        "PACK_ALIGNMENT",
+        "PAGE_RULE",
+        "PARSE_ERR",
+        "PATHSEG_ARC_ABS",
+        "PATHSEG_ARC_REL",
+        "PATHSEG_CLOSEPATH",
+        "PATHSEG_CURVETO_CUBIC_ABS",
+        "PATHSEG_CURVETO_CUBIC_REL",
+        "PATHSEG_CURVETO_CUBIC_SMOOTH_ABS",
+        "PATHSEG_CURVETO_CUBIC_SMOOTH_REL",
+        "PATHSEG_CURVETO_QUADRATIC_ABS",
+        "PATHSEG_CURVETO_QUADRATIC_REL",
+        "PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS",
+        "PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL",
+        "PATHSEG_LINETO_ABS",
+        "PATHSEG_LINETO_HORIZONTAL_ABS",
+        "PATHSEG_LINETO_HORIZONTAL_REL",
+        "PATHSEG_LINETO_REL",
+        "PATHSEG_LINETO_VERTICAL_ABS",
+        "PATHSEG_LINETO_VERTICAL_REL",
+        "PATHSEG_MOVETO_ABS",
+        "PATHSEG_MOVETO_REL",
+        "PATHSEG_UNKNOWN",
+        "PATH_EXISTS_ERR",
+        "PEAKING",
+        "PERMISSION_DENIED",
+        "PERSISTENT",
+        "PI",
+        "PLAYING_STATE",
+        "POINTS",
+        "POLYGON_OFFSET_FACTOR",
+        "POLYGON_OFFSET_FILL",
+        "POLYGON_OFFSET_UNITS",
+        "POSITION_UNAVAILABLE",
+        "POSITIVE_INFINITY",
+        "PREV",
+        "PREV_NO_DUPLICATE",
+        "PROCESSING_INSTRUCTION_NODE",
+        "PageChangeEvent",
+        "PageTransitionEvent",
+        "PaintRequest",
+        "PaintRequestList",
+        "PannerNode",
+        "Path2D",
+        "Performance",
+        "PerformanceEntry",
+        "PerformanceMark",
+        "PerformanceMeasure",
+        "PerformanceNavigation",
+        "PerformanceResourceTiming",
+        "PerformanceTiming",
+        "PeriodicWave",
+        "Plugin",
+        "PluginArray",
+        "PopStateEvent",
+        "PopupBlockedEvent",
+        "ProcessingInstruction",
+        "ProgressEvent",
+        "Promise",
+        "PropertyNodeList",
+        "Proxy",
+        "PushManager",
+        "PushSubscription",
+        "Q",
+        "QUOTA_ERR",
+        "QUOTA_EXCEEDED_ERR",
+        "QueryInterface",
+        "READ_ONLY",
+        "READ_ONLY_ERR",
+        "READ_WRITE",
+        "RED_BITS",
+        "REMOVAL",
+        "RENDERBUFFER",
+        "RENDERBUFFER_ALPHA_SIZE",
+        "RENDERBUFFER_BINDING",
+        "RENDERBUFFER_BLUE_SIZE",
+        "RENDERBUFFER_DEPTH_SIZE",
+        "RENDERBUFFER_GREEN_SIZE",
+        "RENDERBUFFER_HEIGHT",
+        "RENDERBUFFER_INTERNAL_FORMAT",
+        "RENDERBUFFER_RED_SIZE",
+        "RENDERBUFFER_STENCIL_SIZE",
+        "RENDERBUFFER_WIDTH",
+        "RENDERER",
+        "RENDERING_INTENT_ABSOLUTE_COLORIMETRIC",
+        "RENDERING_INTENT_AUTO",
+        "RENDERING_INTENT_PERCEPTUAL",
+        "RENDERING_INTENT_RELATIVE_COLORIMETRIC",
+        "RENDERING_INTENT_SATURATION",
+        "RENDERING_INTENT_UNKNOWN",
+        "REPEAT",
+        "REPLACE",
+        "RGB",
+        "RGB565",
+        "RGB5_A1",
+        "RGBA",
+        "RGBA4",
+        "RGBColor",
+        "ROTATION_CLOCKWISE",
+        "ROTATION_COUNTERCLOCKWISE",
+        "RTCDataChannelEvent",
+        "RTCIceCandidate",
+        "RTCPeerConnectionIceEvent",
+        "RTCRtpReceiver",
+        "RTCRtpSender",
+        "RTCSessionDescription",
+        "RTCStatsReport",
+        "RadioNodeList",
+        "Range",
+        "RangeError",
+        "RangeException",
+        "RecordErrorEvent",
+        "Rect",
+        "ReferenceError",
+        "RegExp",
+        "Request",
+        "Response",
+        "SAMPLER_2D",
+        "SAMPLER_CUBE",
+        "SAMPLES",
+        "SAMPLE_ALPHA_TO_COVERAGE",
+        "SAMPLE_BUFFERS",
+        "SAMPLE_COVERAGE",
+        "SAMPLE_COVERAGE_INVERT",
+        "SAMPLE_COVERAGE_VALUE",
+        "SAWTOOTH",
+        "SCHEDULED_STATE",
+        "SCISSOR_BOX",
+        "SCISSOR_TEST",
+        "SCROLL_PAGE_DOWN",
+        "SCROLL_PAGE_UP",
+        "SDP_ANSWER",
+        "SDP_OFFER",
+        "SDP_PRANSWER",
+        "SECURITY_ERR",
+        "SELECT",
+        "SERIALIZE_ERR",
+        "SEVERITY_ERROR",
+        "SEVERITY_FATAL_ERROR",
+        "SEVERITY_WARNING",
+        "SHADER_COMPILER",
+        "SHADER_TYPE",
+        "SHADING_LANGUAGE_VERSION",
+        "SHIFT_MASK",
+        "SHORT",
+        "SHOWING",
+        "SHOW_ALL",
+        "SHOW_ATTRIBUTE",
+        "SHOW_CDATA_SECTION",
+        "SHOW_COMMENT",
+        "SHOW_DOCUMENT",
+        "SHOW_DOCUMENT_FRAGMENT",
+        "SHOW_DOCUMENT_TYPE",
+        "SHOW_ELEMENT",
+        "SHOW_ENTITY",
+        "SHOW_ENTITY_REFERENCE",
+        "SHOW_NOTATION",
+        "SHOW_PROCESSING_INSTRUCTION",
+        "SHOW_TEXT",
+        "SINE",
+        "SOUNDFIELD",
+        "SQLException",
+        "SQRT1_2",
+        "SQRT2",
+        "SQUARE",
+        "SRC_ALPHA",
+        "SRC_ALPHA_SATURATE",
+        "SRC_COLOR",
+        "START_TO_END",
+        "START_TO_START",
+        "STATIC_DRAW",
+        "STENCIL_ATTACHMENT",
+        "STENCIL_BACK_FAIL",
+        "STENCIL_BACK_FUNC",
+        "STENCIL_BACK_PASS_DEPTH_FAIL",
+        "STENCIL_BACK_PASS_DEPTH_PASS",
+        "STENCIL_BACK_REF",
+        "STENCIL_BACK_VALUE_MASK",
+        "STENCIL_BACK_WRITEMASK",
+        "STENCIL_BITS",
+        "STENCIL_BUFFER_BIT",
+        "STENCIL_CLEAR_VALUE",
+        "STENCIL_FAIL",
+        "STENCIL_FUNC",
+        "STENCIL_INDEX",
+        "STENCIL_INDEX8",
+        "STENCIL_PASS_DEPTH_FAIL",
+        "STENCIL_PASS_DEPTH_PASS",
+        "STENCIL_REF",
+        "STENCIL_TEST",
+        "STENCIL_VALUE_MASK",
+        "STENCIL_WRITEMASK",
+        "STREAM_DRAW",
+        "STRING_TYPE",
+        "STYLE_RULE",
+        "SUBPIXEL_BITS",
+        "SUPPORTS_RULE",
+        "SVGAElement",
+        "SVGAltGlyphDefElement",
+        "SVGAltGlyphElement",
+        "SVGAltGlyphItemElement",
+        "SVGAngle",
+        "SVGAnimateColorElement",
+        "SVGAnimateElement",
+        "SVGAnimateMotionElement",
+        "SVGAnimateTransformElement",
+        "SVGAnimatedAngle",
+        "SVGAnimatedBoolean",
+        "SVGAnimatedEnumeration",
+        "SVGAnimatedInteger",
+        "SVGAnimatedLength",
+        "SVGAnimatedLengthList",
+        "SVGAnimatedNumber",
+        "SVGAnimatedNumberList",
+        "SVGAnimatedPreserveAspectRatio",
+        "SVGAnimatedRect",
+        "SVGAnimatedString",
+        "SVGAnimatedTransformList",
+        "SVGAnimationElement",
+        "SVGCircleElement",
+        "SVGClipPathElement",
+        "SVGColor",
+        "SVGComponentTransferFunctionElement",
+        "SVGCursorElement",
+        "SVGDefsElement",
+        "SVGDescElement",
+        "SVGDiscardElement",
+        "SVGDocument",
+        "SVGElement",
+        "SVGElementInstance",
+        "SVGElementInstanceList",
+        "SVGEllipseElement",
+        "SVGException",
+        "SVGFEBlendElement",
+        "SVGFEColorMatrixElement",
+        "SVGFEComponentTransferElement",
+        "SVGFECompositeElement",
+        "SVGFEConvolveMatrixElement",
+        "SVGFEDiffuseLightingElement",
+        "SVGFEDisplacementMapElement",
+        "SVGFEDistantLightElement",
+        "SVGFEDropShadowElement",
+        "SVGFEFloodElement",
+        "SVGFEFuncAElement",
+        "SVGFEFuncBElement",
+        "SVGFEFuncGElement",
+        "SVGFEFuncRElement",
+        "SVGFEGaussianBlurElement",
+        "SVGFEImageElement",
+        "SVGFEMergeElement",
+        "SVGFEMergeNodeElement",
+        "SVGFEMorphologyElement",
+        "SVGFEOffsetElement",
+        "SVGFEPointLightElement",
+        "SVGFESpecularLightingElement",
+        "SVGFESpotLightElement",
+        "SVGFETileElement",
+        "SVGFETurbulenceElement",
+        "SVGFilterElement",
+        "SVGFontElement",
+        "SVGFontFaceElement",
+        "SVGFontFaceFormatElement",
+        "SVGFontFaceNameElement",
+        "SVGFontFaceSrcElement",
+        "SVGFontFaceUriElement",
+        "SVGForeignObjectElement",
+        "SVGGElement",
+        "SVGGeometryElement",
+        "SVGGlyphElement",
+        "SVGGlyphRefElement",
+        "SVGGradientElement",
+        "SVGGraphicsElement",
+        "SVGHKernElement",
+        "SVGImageElement",
+        "SVGLength",
+        "SVGLengthList",
+        "SVGLineElement",
+        "SVGLinearGradientElement",
+        "SVGMPathElement",
+        "SVGMarkerElement",
+        "SVGMaskElement",
+        "SVGMatrix",
+        "SVGMetadataElement",
+        "SVGMissingGlyphElement",
+        "SVGNumber",
+        "SVGNumberList",
+        "SVGPaint",
+        "SVGPathElement",
+        "SVGPathSeg",
+        "SVGPathSegArcAbs",
+        "SVGPathSegArcRel",
+        "SVGPathSegClosePath",
+        "SVGPathSegCurvetoCubicAbs",
+        "SVGPathSegCurvetoCubicRel",
+        "SVGPathSegCurvetoCubicSmoothAbs",
+        "SVGPathSegCurvetoCubicSmoothRel",
+        "SVGPathSegCurvetoQuadraticAbs",
+        "SVGPathSegCurvetoQuadraticRel",
+        "SVGPathSegCurvetoQuadraticSmoothAbs",
+        "SVGPathSegCurvetoQuadraticSmoothRel",
+        "SVGPathSegLinetoAbs",
+        "SVGPathSegLinetoHorizontalAbs",
+        "SVGPathSegLinetoHorizontalRel",
+        "SVGPathSegLinetoRel",
+        "SVGPathSegLinetoVerticalAbs",
+        "SVGPathSegLinetoVerticalRel",
+        "SVGPathSegList",
+        "SVGPathSegMovetoAbs",
+        "SVGPathSegMovetoRel",
+        "SVGPatternElement",
+        "SVGPoint",
+        "SVGPointList",
+        "SVGPolygonElement",
+        "SVGPolylineElement",
+        "SVGPreserveAspectRatio",
+        "SVGRadialGradientElement",
+        "SVGRect",
+        "SVGRectElement",
+        "SVGRenderingIntent",
+        "SVGSVGElement",
+        "SVGScriptElement",
+        "SVGSetElement",
+        "SVGStopElement",
+        "SVGStringList",
+        "SVGStyleElement",
+        "SVGSwitchElement",
+        "SVGSymbolElement",
+        "SVGTRefElement",
+        "SVGTSpanElement",
+        "SVGTextContentElement",
+        "SVGTextElement",
+        "SVGTextPathElement",
+        "SVGTextPositioningElement",
+        "SVGTitleElement",
+        "SVGTransform",
+        "SVGTransformList",
+        "SVGUnitTypes",
+        "SVGUseElement",
+        "SVGVKernElement",
+        "SVGViewElement",
+        "SVGViewSpec",
+        "SVGZoomAndPan",
+        "SVGZoomEvent",
+        "SVG_ANGLETYPE_DEG",
+        "SVG_ANGLETYPE_GRAD",
+        "SVG_ANGLETYPE_RAD",
+        "SVG_ANGLETYPE_UNKNOWN",
+        "SVG_ANGLETYPE_UNSPECIFIED",
+        "SVG_CHANNEL_A",
+        "SVG_CHANNEL_B",
+        "SVG_CHANNEL_G",
+        "SVG_CHANNEL_R",
+        "SVG_CHANNEL_UNKNOWN",
+        "SVG_COLORTYPE_CURRENTCOLOR",
+        "SVG_COLORTYPE_RGBCOLOR",
+        "SVG_COLORTYPE_RGBCOLOR_ICCCOLOR",
+        "SVG_COLORTYPE_UNKNOWN",
+        "SVG_EDGEMODE_DUPLICATE",
+        "SVG_EDGEMODE_NONE",
+        "SVG_EDGEMODE_UNKNOWN",
+        "SVG_EDGEMODE_WRAP",
+        "SVG_FEBLEND_MODE_COLOR",
+        "SVG_FEBLEND_MODE_COLOR_BURN",
+        "SVG_FEBLEND_MODE_COLOR_DODGE",
+        "SVG_FEBLEND_MODE_DARKEN",
+        "SVG_FEBLEND_MODE_DIFFERENCE",
+        "SVG_FEBLEND_MODE_EXCLUSION",
+        "SVG_FEBLEND_MODE_HARD_LIGHT",
+        "SVG_FEBLEND_MODE_HUE",
+        "SVG_FEBLEND_MODE_LIGHTEN",
+        "SVG_FEBLEND_MODE_LUMINOSITY",
+        "SVG_FEBLEND_MODE_MULTIPLY",
+        "SVG_FEBLEND_MODE_NORMAL",
+        "SVG_FEBLEND_MODE_OVERLAY",
+        "SVG_FEBLEND_MODE_SATURATION",
+        "SVG_FEBLEND_MODE_SCREEN",
+        "SVG_FEBLEND_MODE_SOFT_LIGHT",
+        "SVG_FEBLEND_MODE_UNKNOWN",
+        "SVG_FECOLORMATRIX_TYPE_HUEROTATE",
+        "SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA",
+        "SVG_FECOLORMATRIX_TYPE_MATRIX",
+        "SVG_FECOLORMATRIX_TYPE_SATURATE",
+        "SVG_FECOLORMATRIX_TYPE_UNKNOWN",
+        "SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE",
+        "SVG_FECOMPONENTTRANSFER_TYPE_GAMMA",
+        "SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY",
+        "SVG_FECOMPONENTTRANSFER_TYPE_LINEAR",
+        "SVG_FECOMPONENTTRANSFER_TYPE_TABLE",
+        "SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN",
+        "SVG_FECOMPOSITE_OPERATOR_ARITHMETIC",
+        "SVG_FECOMPOSITE_OPERATOR_ATOP",
+        "SVG_FECOMPOSITE_OPERATOR_IN",
+        "SVG_FECOMPOSITE_OPERATOR_OUT",
+        "SVG_FECOMPOSITE_OPERATOR_OVER",
+        "SVG_FECOMPOSITE_OPERATOR_UNKNOWN",
+        "SVG_FECOMPOSITE_OPERATOR_XOR",
+        "SVG_INVALID_VALUE_ERR",
+        "SVG_LENGTHTYPE_CM",
+        "SVG_LENGTHTYPE_EMS",
+        "SVG_LENGTHTYPE_EXS",
+        "SVG_LENGTHTYPE_IN",
+        "SVG_LENGTHTYPE_MM",
+        "SVG_LENGTHTYPE_NUMBER",
+        "SVG_LENGTHTYPE_PC",
+        "SVG_LENGTHTYPE_PERCENTAGE",
+        "SVG_LENGTHTYPE_PT",
+        "SVG_LENGTHTYPE_PX",
+        "SVG_LENGTHTYPE_UNKNOWN",
+        "SVG_MARKERUNITS_STROKEWIDTH",
+        "SVG_MARKERUNITS_UNKNOWN",
+        "SVG_MARKERUNITS_USERSPACEONUSE",
+        "SVG_MARKER_ORIENT_ANGLE",
+        "SVG_MARKER_ORIENT_AUTO",
+        "SVG_MARKER_ORIENT_UNKNOWN",
+        "SVG_MASKTYPE_ALPHA",
+        "SVG_MASKTYPE_LUMINANCE",
+        "SVG_MATRIX_NOT_INVERTABLE",
+        "SVG_MEETORSLICE_MEET",
+        "SVG_MEETORSLICE_SLICE",
+        "SVG_MEETORSLICE_UNKNOWN",
+        "SVG_MORPHOLOGY_OPERATOR_DILATE",
+        "SVG_MORPHOLOGY_OPERATOR_ERODE",
+        "SVG_MORPHOLOGY_OPERATOR_UNKNOWN",
+        "SVG_PAINTTYPE_CURRENTCOLOR",
+        "SVG_PAINTTYPE_NONE",
+        "SVG_PAINTTYPE_RGBCOLOR",
+        "SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR",
+        "SVG_PAINTTYPE_UNKNOWN",
+        "SVG_PAINTTYPE_URI",
+        "SVG_PAINTTYPE_URI_CURRENTCOLOR",
+        "SVG_PAINTTYPE_URI_NONE",
+        "SVG_PAINTTYPE_URI_RGBCOLOR",
+        "SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR",
+        "SVG_PRESERVEASPECTRATIO_NONE",
+        "SVG_PRESERVEASPECTRATIO_UNKNOWN",
+        "SVG_PRESERVEASPECTRATIO_XMAXYMAX",
+        "SVG_PRESERVEASPECTRATIO_XMAXYMID",
+        "SVG_PRESERVEASPECTRATIO_XMAXYMIN",
+        "SVG_PRESERVEASPECTRATIO_XMIDYMAX",
+        "SVG_PRESERVEASPECTRATIO_XMIDYMID",
+        "SVG_PRESERVEASPECTRATIO_XMIDYMIN",
+        "SVG_PRESERVEASPECTRATIO_XMINYMAX",
+        "SVG_PRESERVEASPECTRATIO_XMINYMID",
+        "SVG_PRESERVEASPECTRATIO_XMINYMIN",
+        "SVG_SPREADMETHOD_PAD",
+        "SVG_SPREADMETHOD_REFLECT",
+        "SVG_SPREADMETHOD_REPEAT",
+        "SVG_SPREADMETHOD_UNKNOWN",
+        "SVG_STITCHTYPE_NOSTITCH",
+        "SVG_STITCHTYPE_STITCH",
+        "SVG_STITCHTYPE_UNKNOWN",
+        "SVG_TRANSFORM_MATRIX",
+        "SVG_TRANSFORM_ROTATE",
+        "SVG_TRANSFORM_SCALE",
+        "SVG_TRANSFORM_SKEWX",
+        "SVG_TRANSFORM_SKEWY",
+        "SVG_TRANSFORM_TRANSLATE",
+        "SVG_TRANSFORM_UNKNOWN",
+        "SVG_TURBULENCE_TYPE_FRACTALNOISE",
+        "SVG_TURBULENCE_TYPE_TURBULENCE",
+        "SVG_TURBULENCE_TYPE_UNKNOWN",
+        "SVG_UNIT_TYPE_OBJECTBOUNDINGBOX",
+        "SVG_UNIT_TYPE_UNKNOWN",
+        "SVG_UNIT_TYPE_USERSPACEONUSE",
+        "SVG_WRONG_TYPE_ERR",
+        "SVG_ZOOMANDPAN_DISABLE",
+        "SVG_ZOOMANDPAN_MAGNIFY",
+        "SVG_ZOOMANDPAN_UNKNOWN",
+        "SYNTAX_ERR",
+        "SavedPages",
+        "Screen",
+        "ScreenOrientation",
+        "Script",
+        "ScriptProcessorNode",
+        "ScrollAreaEvent",
+        "SecurityPolicyViolationEvent",
+        "Selection",
+        "ServiceWorker",
+        "ServiceWorkerContainer",
+        "ServiceWorkerRegistration",
+        "SessionDescription",
+        "Set",
+        "ShadowRoot",
+        "SharedWorker",
+        "SimpleGestureEvent",
+        "SpeechSynthesisEvent",
+        "SpeechSynthesisUtterance",
+        "StopIteration",
+        "Storage",
+        "StorageEvent",
+        "String",
+        "StyleSheet",
+        "StyleSheetList",
+        "SubtleCrypto",
+        "Symbol",
+        "SyntaxError",
+        "TEMPORARY",
+        "TEXTPATH_METHODTYPE_ALIGN",
+        "TEXTPATH_METHODTYPE_STRETCH",
+        "TEXTPATH_METHODTYPE_UNKNOWN",
+        "TEXTPATH_SPACINGTYPE_AUTO",
+        "TEXTPATH_SPACINGTYPE_EXACT",
+        "TEXTPATH_SPACINGTYPE_UNKNOWN",
+        "TEXTURE",
+        "TEXTURE0",
+        "TEXTURE1",
+        "TEXTURE10",
+        "TEXTURE11",
+        "TEXTURE12",
+        "TEXTURE13",
+        "TEXTURE14",
+        "TEXTURE15",
+        "TEXTURE16",
+        "TEXTURE17",
+        "TEXTURE18",
+        "TEXTURE19",
+        "TEXTURE2",
+        "TEXTURE20",
+        "TEXTURE21",
+        "TEXTURE22",
+        "TEXTURE23",
+        "TEXTURE24",
+        "TEXTURE25",
+        "TEXTURE26",
+        "TEXTURE27",
+        "TEXTURE28",
+        "TEXTURE29",
+        "TEXTURE3",
+        "TEXTURE30",
+        "TEXTURE31",
+        "TEXTURE4",
+        "TEXTURE5",
+        "TEXTURE6",
+        "TEXTURE7",
+        "TEXTURE8",
+        "TEXTURE9",
+        "TEXTURE_2D",
+        "TEXTURE_BINDING_2D",
+        "TEXTURE_BINDING_CUBE_MAP",
+        "TEXTURE_CUBE_MAP",
+        "TEXTURE_CUBE_MAP_NEGATIVE_X",
+        "TEXTURE_CUBE_MAP_NEGATIVE_Y",
+        "TEXTURE_CUBE_MAP_NEGATIVE_Z",
+        "TEXTURE_CUBE_MAP_POSITIVE_X",
+        "TEXTURE_CUBE_MAP_POSITIVE_Y",
+        "TEXTURE_CUBE_MAP_POSITIVE_Z",
+        "TEXTURE_MAG_FILTER",
+        "TEXTURE_MAX_ANISOTROPY_EXT",
+        "TEXTURE_MIN_FILTER",
+        "TEXTURE_WRAP_S",
+        "TEXTURE_WRAP_T",
+        "TEXT_NODE",
+        "TIMEOUT",
+        "TIMEOUT_ERR",
+        "TOO_LARGE_ERR",
+        "TRANSACTION_INACTIVE_ERR",
+        "TRIANGLE",
+        "TRIANGLES",
+        "TRIANGLE_FAN",
+        "TRIANGLE_STRIP",
+        "TYPE_BACK_FORWARD",
+        "TYPE_ERR",
+        "TYPE_MISMATCH_ERR",
+        "TYPE_NAVIGATE",
+        "TYPE_RELOAD",
+        "TYPE_RESERVED",
+        "Text",
+        "TextDecoder",
+        "TextEncoder",
+        "TextEvent",
+        "TextMetrics",
+        "TextTrack",
+        "TextTrackCue",
+        "TextTrackCueList",
+        "TextTrackList",
+        "TimeEvent",
+        "TimeRanges",
+        "Touch",
+        "TouchEvent",
+        "TouchList",
+        "TrackEvent",
+        "TransitionEvent",
+        "TreeWalker",
+        "TypeError",
+        "UIEvent",
+        "UNCACHED",
+        "UNKNOWN_ERR",
+        "UNKNOWN_RULE",
+        "UNMASKED_RENDERER_WEBGL",
+        "UNMASKED_VENDOR_WEBGL",
+        "UNORDERED_NODE_ITERATOR_TYPE",
+        "UNORDERED_NODE_SNAPSHOT_TYPE",
+        "UNPACK_ALIGNMENT",
+        "UNPACK_COLORSPACE_CONVERSION_WEBGL",
+        "UNPACK_FLIP_Y_WEBGL",
+        "UNPACK_PREMULTIPLY_ALPHA_WEBGL",
+        "UNSCHEDULED_STATE",
+        "UNSENT",
+        "UNSIGNED_BYTE",
+        "UNSIGNED_INT",
+        "UNSIGNED_SHORT",
+        "UNSIGNED_SHORT_4_4_4_4",
+        "UNSIGNED_SHORT_5_5_5_1",
+        "UNSIGNED_SHORT_5_6_5",
+        "UNSPECIFIED_EVENT_TYPE_ERR",
+        "UPDATEREADY",
+        "URIError",
+        "URL",
+        "URLSearchParams",
+        "URLUnencoded",
+        "URL_MISMATCH_ERR",
+        "UTC",
+        "Uint16Array",
+        "Uint32Array",
+        "Uint8Array",
+        "Uint8ClampedArray",
+        "UserMessageHandler",
+        "UserMessageHandlersNamespace",
+        "UserProximityEvent",
+        "VALIDATE_STATUS",
+        "VALIDATION_ERR",
+        "VARIABLES_RULE",
+        "VENDOR",
+        "VERSION",
+        "VERSION_CHANGE",
+        "VERSION_ERR",
+        "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING",
+        "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE",
+        "VERTEX_ATTRIB_ARRAY_ENABLED",
+        "VERTEX_ATTRIB_ARRAY_NORMALIZED",
+        "VERTEX_ATTRIB_ARRAY_POINTER",
+        "VERTEX_ATTRIB_ARRAY_SIZE",
+        "VERTEX_ATTRIB_ARRAY_STRIDE",
+        "VERTEX_ATTRIB_ARRAY_TYPE",
+        "VERTEX_SHADER",
+        "VERTICAL",
+        "VERTICAL_AXIS",
+        "VER_ERR",
+        "VIEWPORT",
+        "VIEWPORT_RULE",
+        "VTTCue",
+        "VTTRegion",
+        "ValidityState",
+        "VideoStreamTrack",
+        "WEBKIT_FILTER_RULE",
+        "WEBKIT_KEYFRAMES_RULE",
+        "WEBKIT_KEYFRAME_RULE",
+        "WEBKIT_REGION_RULE",
+        "WRONG_DOCUMENT_ERR",
+        "WaveShaperNode",
+        "WeakMap",
+        "WeakSet",
+        "WebGLActiveInfo",
+        "WebGLBuffer",
+        "WebGLContextEvent",
+        "WebGLFramebuffer",
+        "WebGLProgram",
+        "WebGLRenderbuffer",
+        "WebGLRenderingContext",
+        "WebGLShader",
+        "WebGLShaderPrecisionFormat",
+        "WebGLTexture",
+        "WebGLUniformLocation",
+        "WebGLVertexArray",
+        "WebKitAnimationEvent",
+        "WebKitBlobBuilder",
+        "WebKitCSSFilterRule",
+        "WebKitCSSFilterValue",
+        "WebKitCSSKeyframeRule",
+        "WebKitCSSKeyframesRule",
+        "WebKitCSSMatrix",
+        "WebKitCSSRegionRule",
+        "WebKitCSSTransformValue",
+        "WebKitDataCue",
+        "WebKitGamepad",
+        "WebKitMediaKeyError",
+        "WebKitMediaKeyMessageEvent",
+        "WebKitMediaKeySession",
+        "WebKitMediaKeys",
+        "WebKitMediaSource",
+        "WebKitMutationObserver",
+        "WebKitNamespace",
+        "WebKitPlaybackTargetAvailabilityEvent",
+        "WebKitPoint",
+        "WebKitShadowRoot",
+        "WebKitSourceBuffer",
+        "WebKitSourceBufferList",
+        "WebKitTransitionEvent",
+        "WebSocket",
+        "WheelEvent",
+        "Window",
+        "Worker",
+        "XMLDocument",
+        "XMLHttpRequest",
+        "XMLHttpRequestEventTarget",
+        "XMLHttpRequestException",
+        "XMLHttpRequestProgressEvent",
+        "XMLHttpRequestUpload",
+        "XMLSerializer",
+        "XMLStylesheetProcessingInstruction",
+        "XPathEvaluator",
+        "XPathException",
+        "XPathExpression",
+        "XPathNSResolver",
+        "XPathResult",
+        "XSLTProcessor",
+        "ZERO",
+        "_XD0M_",
+        "_YD0M_",
+        "__defineGetter__",
+        "__defineSetter__",
+        "__lookupGetter__",
+        "__lookupSetter__",
+        "__opera",
+        "__proto__",
+        "_browserjsran",
+        "a",
+        "aLink",
+        "abbr",
+        "abort",
+        "abs",
+        "absolute",
+        "acceleration",
+        "accelerationIncludingGravity",
+        "accelerator",
+        "accept",
+        "acceptCharset",
+        "acceptNode",
+        "accessKey",
+        "accessKeyLabel",
+        "accuracy",
+        "acos",
+        "acosh",
+        "action",
+        "actionURL",
+        "active",
+        "activeCues",
+        "activeElement",
+        "activeSourceBuffers",
+        "activeSourceCount",
+        "activeTexture",
+        "add",
+        "addBehavior",
+        "addCandidate",
+        "addColorStop",
+        "addCue",
+        "addElement",
+        "addEventListener",
+        "addFilter",
+        "addFromString",
+        "addFromUri",
+        "addIceCandidate",
+        "addImport",
+        "addListener",
+        "addNamed",
+        "addPageRule",
+        "addPath",
+        "addPointer",
+        "addRange",
+        "addRegion",
+        "addRule",
+        "addSearchEngine",
+        "addSourceBuffer",
+        "addStream",
+        "addTextTrack",
+        "addTrack",
+        "addWakeLockListener",
+        "addedNodes",
+        "additionalName",
+        "additiveSymbols",
+        "addons",
+        "adoptNode",
+        "adr",
+        "advance",
+        "alert",
+        "algorithm",
+        "align",
+        "align-content",
+        "align-items",
+        "align-self",
+        "alignContent",
+        "alignItems",
+        "alignSelf",
+        "alignmentBaseline",
+        "alinkColor",
+        "all",
+        "allowFullscreen",
+        "allowedDirections",
+        "alpha",
+        "alt",
+        "altGraphKey",
+        "altHtml",
+        "altKey",
+        "altLeft",
+        "altitude",
+        "altitudeAccuracy",
+        "amplitude",
+        "ancestorOrigins",
+        "anchor",
+        "anchorNode",
+        "anchorOffset",
+        "anchors",
+        "angle",
+        "animVal",
+        "animate",
+        "animatedInstanceRoot",
+        "animatedNormalizedPathSegList",
+        "animatedPathSegList",
+        "animatedPoints",
+        "animation",
+        "animation-delay",
+        "animation-direction",
+        "animation-duration",
+        "animation-fill-mode",
+        "animation-iteration-count",
+        "animation-name",
+        "animation-play-state",
+        "animation-timing-function",
+        "animationDelay",
+        "animationDirection",
+        "animationDuration",
+        "animationFillMode",
+        "animationIterationCount",
+        "animationName",
+        "animationPlayState",
+        "animationStartTime",
+        "animationTimingFunction",
+        "animationsPaused",
+        "anniversary",
+        "app",
+        "appCodeName",
+        "appMinorVersion",
+        "appName",
+        "appNotifications",
+        "appVersion",
+        "append",
+        "appendBuffer",
+        "appendChild",
+        "appendData",
+        "appendItem",
+        "appendMedium",
+        "appendNamed",
+        "appendRule",
+        "appendStream",
+        "appendWindowEnd",
+        "appendWindowStart",
+        "applets",
+        "applicationCache",
+        "apply",
+        "applyElement",
+        "arc",
+        "arcTo",
+        "archive",
+        "areas",
+        "arguments",
+        "arrayBuffer",
+        "asin",
+        "asinh",
+        "assert",
+        "assign",
+        "async",
+        "atEnd",
+        "atan",
+        "atan2",
+        "atanh",
+        "atob",
+        "attachEvent",
+        "attachShader",
+        "attachments",
+        "attack",
+        "attrChange",
+        "attrName",
+        "attributeName",
+        "attributeNamespace",
+        "attributes",
+        "audioTracks",
+        "autoIncrement",
+        "autobuffer",
+        "autocapitalize",
+        "autocomplete",
+        "autocorrect",
+        "autofocus",
+        "autoplay",
+        "availHeight",
+        "availLeft",
+        "availTop",
+        "availWidth",
+        "availability",
+        "available",
+        "aversion",
+        "axes",
+        "axis",
+        "azimuth",
+        "b",
+        "back",
+        "backface-visibility",
+        "backfaceVisibility",
+        "background",
+        "background-attachment",
+        "background-blend-mode",
+        "background-clip",
+        "background-color",
+        "background-image",
+        "background-origin",
+        "background-position",
+        "background-repeat",
+        "background-size",
+        "backgroundAttachment",
+        "backgroundBlendMode",
+        "backgroundClip",
+        "backgroundColor",
+        "backgroundImage",
+        "backgroundOrigin",
+        "backgroundPosition",
+        "backgroundPositionX",
+        "backgroundPositionY",
+        "backgroundRepeat",
+        "backgroundSize",
+        "badInput",
+        "balance",
+        "baseFrequencyX",
+        "baseFrequencyY",
+        "baseNode",
+        "baseOffset",
+        "baseURI",
+        "baseVal",
+        "baselineShift",
+        "battery",
+        "bday",
+        "beginElement",
+        "beginElementAt",
+        "beginPath",
+        "behavior",
+        "behaviorCookie",
+        "behaviorPart",
+        "behaviorUrns",
+        "beta",
+        "bezierCurveTo",
+        "bgColor",
+        "bgProperties",
+        "bias",
+        "big",
+        "binaryType",
+        "bind",
+        "bindAttribLocation",
+        "bindBuffer",
+        "bindFramebuffer",
+        "bindRenderbuffer",
+        "bindTexture",
+        "blendColor",
+        "blendEquation",
+        "blendEquationSeparate",
+        "blendFunc",
+        "blendFuncSeparate",
+        "blink",
+        "blob",
+        "blockDirection",
+        "blue",
+        "blur",
+        "body",
+        "bodyUsed",
+        "bold",
+        "bookmarks",
+        "booleanValue",
+        "border",
+        "border-bottom",
+        "border-bottom-color",
+        "border-bottom-left-radius",
+        "border-bottom-right-radius",
+        "border-bottom-style",
+        "border-bottom-width",
+        "border-collapse",
+        "border-color",
+        "border-image",
+        "border-image-outset",
+        "border-image-repeat",
+        "border-image-slice",
+        "border-image-source",
+        "border-image-width",
+        "border-left",
+        "border-left-color",
+        "border-left-style",
+        "border-left-width",
+        "border-radius",
+        "border-right",
+        "border-right-color",
+        "border-right-style",
+        "border-right-width",
+        "border-spacing",
+        "border-style",
+        "border-top",
+        "border-top-color",
+        "border-top-left-radius",
+        "border-top-right-radius",
+        "border-top-style",
+        "border-top-width",
+        "border-width",
+        "borderBottom",
+        "borderBottomColor",
+        "borderBottomLeftRadius",
+        "borderBottomRightRadius",
+        "borderBottomStyle",
+        "borderBottomWidth",
+        "borderCollapse",
+        "borderColor",
+        "borderColorDark",
+        "borderColorLight",
+        "borderImage",
+        "borderImageOutset",
+        "borderImageRepeat",
+        "borderImageSlice",
+        "borderImageSource",
+        "borderImageWidth",
+        "borderLeft",
+        "borderLeftColor",
+        "borderLeftStyle",
+        "borderLeftWidth",
+        "borderRadius",
+        "borderRight",
+        "borderRightColor",
+        "borderRightStyle",
+        "borderRightWidth",
+        "borderSpacing",
+        "borderStyle",
+        "borderTop",
+        "borderTopColor",
+        "borderTopLeftRadius",
+        "borderTopRightRadius",
+        "borderTopStyle",
+        "borderTopWidth",
+        "borderWidth",
+        "bottom",
+        "bottomMargin",
+        "bound",
+        "boundElements",
+        "boundingClientRect",
+        "boundingHeight",
+        "boundingLeft",
+        "boundingTop",
+        "boundingWidth",
+        "bounds",
+        "box-decoration-break",
+        "box-shadow",
+        "box-sizing",
+        "boxDecorationBreak",
+        "boxShadow",
+        "boxSizing",
+        "breakAfter",
+        "breakBefore",
+        "breakInside",
+        "browserLanguage",
+        "btoa",
+        "bubbles",
+        "buffer",
+        "bufferData",
+        "bufferDepth",
+        "bufferSize",
+        "bufferSubData",
+        "buffered",
+        "bufferedAmount",
+        "buildID",
+        "buildNumber",
+        "button",
+        "buttonID",
+        "buttons",
+        "byteLength",
+        "byteOffset",
+        "c",
+        "call",
+        "caller",
+        "canBeFormatted",
+        "canBeMounted",
+        "canBeShared",
+        "canHaveChildren",
+        "canHaveHTML",
+        "canPlayType",
+        "cancel",
+        "cancelAnimationFrame",
+        "cancelBubble",
+        "cancelScheduledValues",
+        "cancelable",
+        "candidate",
+        "canvas",
+        "caption",
+        "caption-side",
+        "captionSide",
+        "captureEvents",
+        "captureStackTrace",
+        "caretPositionFromPoint",
+        "caretRangeFromPoint",
+        "cast",
+        "catch",
+        "category",
+        "cbrt",
+        "cd",
+        "ceil",
+        "cellIndex",
+        "cellPadding",
+        "cellSpacing",
+        "cells",
+        "ch",
+        "chOff",
+        "chain",
+        "challenge",
+        "changedTouches",
+        "channel",
+        "channelCount",
+        "channelCountMode",
+        "channelInterpretation",
+        "char",
+        "charAt",
+        "charCode",
+        "charCodeAt",
+        "charIndex",
+        "characterSet",
+        "charging",
+        "chargingTime",
+        "charset",
+        "checkEnclosure",
+        "checkFramebufferStatus",
+        "checkIntersection",
+        "checkValidity",
+        "checked",
+        "childElementCount",
+        "childNodes",
+        "children",
+        "chrome",
+        "ciphertext",
+        "cite",
+        "classList",
+        "className",
+        "classid",
+        "clear",
+        "clearAttributes",
+        "clearColor",
+        "clearData",
+        "clearDepth",
+        "clearImmediate",
+        "clearInterval",
+        "clearMarks",
+        "clearMeasures",
+        "clearParameters",
+        "clearRect",
+        "clearResourceTimings",
+        "clearShadow",
+        "clearStencil",
+        "clearTimeout",
+        "clearWatch",
+        "click",
+        "clickCount",
+        "clientHeight",
+        "clientInformation",
+        "clientLeft",
+        "clientRect",
+        "clientRects",
+        "clientTop",
+        "clientWidth",
+        "clientX",
+        "clientY",
+        "clip",
+        "clip-path",
+        "clip-rule",
+        "clipBottom",
+        "clipLeft",
+        "clipPath",
+        "clipPathUnits",
+        "clipRight",
+        "clipRule",
+        "clipTop",
+        "clipboardData",
+        "clone",
+        "cloneContents",
+        "cloneNode",
+        "cloneRange",
+        "close",
+        "closePath",
+        "closed",
+        "closest",
+        "clz",
+        "clz32",
+        "cmp",
+        "code",
+        "codeBase",
+        "codePointAt",
+        "codeType",
+        "colSpan",
+        "collapse",
+        "collapseToEnd",
+        "collapseToStart",
+        "collapsed",
+        "collect",
+        "colno",
+        "color",
+        "color-interpolation",
+        "color-interpolation-filters",
+        "colorDepth",
+        "colorInterpolation",
+        "colorInterpolationFilters",
+        "colorMask",
+        "colorType",
+        "cols",
+        "columnCount",
+        "columnFill",
+        "columnGap",
+        "columnNumber",
+        "columnRule",
+        "columnRuleColor",
+        "columnRuleStyle",
+        "columnRuleWidth",
+        "columnSpan",
+        "columnWidth",
+        "columns",
+        "command",
+        "commitPreferences",
+        "commonAncestorContainer",
+        "compact",
+        "compareBoundaryPoints",
+        "compareDocumentPosition",
+        "compareEndPoints",
+        "compareNode",
+        "comparePoint",
+        "compatMode",
+        "compatible",
+        "compile",
+        "compileShader",
+        "complete",
+        "componentFromPoint",
+        "compositionEndOffset",
+        "compositionStartOffset",
+        "compressedTexImage2D",
+        "compressedTexSubImage2D",
+        "concat",
+        "conditionText",
+        "coneInnerAngle",
+        "coneOuterAngle",
+        "coneOuterGain",
+        "confirm",
+        "confirmComposition",
+        "confirmSiteSpecificTrackingException",
+        "confirmWebWideTrackingException",
+        "connect",
+        "connectEnd",
+        "connectStart",
+        "connected",
+        "connection",
+        "connectionSpeed",
+        "console",
+        "consolidate",
+        "constrictionActive",
+        "constructor",
+        "contactID",
+        "contains",
+        "containsNode",
+        "content",
+        "contentDocument",
+        "contentEditable",
+        "contentOverflow",
+        "contentScriptType",
+        "contentStyleType",
+        "contentType",
+        "contentWindow",
+        "context",
+        "contextMenu",
+        "contextmenu",
+        "continue",
+        "continuous",
+        "control",
+        "controller",
+        "controls",
+        "convertToSpecifiedUnits",
+        "cookie",
+        "cookieEnabled",
+        "coords",
+        "copyFromChannel",
+        "copyTexImage2D",
+        "copyTexSubImage2D",
+        "copyToChannel",
+        "copyWithin",
+        "correspondingElement",
+        "correspondingUseElement",
+        "cos",
+        "cosh",
+        "count",
+        "counter-increment",
+        "counter-reset",
+        "counterIncrement",
+        "counterReset",
+        "cpuClass",
+        "cpuSleepAllowed",
+        "create",
+        "createAnalyser",
+        "createAnswer",
+        "createAttribute",
+        "createAttributeNS",
+        "createBiquadFilter",
+        "createBuffer",
+        "createBufferSource",
+        "createCDATASection",
+        "createCSSStyleSheet",
+        "createCaption",
+        "createChannelMerger",
+        "createChannelSplitter",
+        "createComment",
+        "createContextualFragment",
+        "createControlRange",
+        "createConvolver",
+        "createDTMFSender",
+        "createDataChannel",
+        "createDelay",
+        "createDelayNode",
+        "createDocument",
+        "createDocumentFragment",
+        "createDocumentType",
+        "createDynamicsCompressor",
+        "createElement",
+        "createElementNS",
+        "createEntityReference",
+        "createEvent",
+        "createEventObject",
+        "createExpression",
+        "createFramebuffer",
+        "createFunction",
+        "createGain",
+        "createGainNode",
+        "createHTMLDocument",
+        "createImageBitmap",
+        "createImageData",
+        "createIndex",
+        "createJavaScriptNode",
+        "createLinearGradient",
+        "createMediaElementSource",
+        "createMediaKeys",
+        "createMediaStreamDestination",
+        "createMediaStreamSource",
+        "createMutableFile",
+        "createNSResolver",
+        "createNodeIterator",
+        "createNotification",
+        "createObjectStore",
+        "createObjectURL",
+        "createOffer",
+        "createOscillator",
+        "createPanner",
+        "createPattern",
+        "createPeriodicWave",
+        "createPopup",
+        "createProcessingInstruction",
+        "createProgram",
+        "createRadialGradient",
+        "createRange",
+        "createRangeCollection",
+        "createRenderbuffer",
+        "createSVGAngle",
+        "createSVGLength",
+        "createSVGMatrix",
+        "createSVGNumber",
+        "createSVGPathSegArcAbs",
+        "createSVGPathSegArcRel",
+        "createSVGPathSegClosePath",
+        "createSVGPathSegCurvetoCubicAbs",
+        "createSVGPathSegCurvetoCubicRel",
+        "createSVGPathSegCurvetoCubicSmoothAbs",
+        "createSVGPathSegCurvetoCubicSmoothRel",
+        "createSVGPathSegCurvetoQuadraticAbs",
+        "createSVGPathSegCurvetoQuadraticRel",
+        "createSVGPathSegCurvetoQuadraticSmoothAbs",
+        "createSVGPathSegCurvetoQuadraticSmoothRel",
+        "createSVGPathSegLinetoAbs",
+        "createSVGPathSegLinetoHorizontalAbs",
+        "createSVGPathSegLinetoHorizontalRel",
+        "createSVGPathSegLinetoRel",
+        "createSVGPathSegLinetoVerticalAbs",
+        "createSVGPathSegLinetoVerticalRel",
+        "createSVGPathSegMovetoAbs",
+        "createSVGPathSegMovetoRel",
+        "createSVGPoint",
+        "createSVGRect",
+        "createSVGTransform",
+        "createSVGTransformFromMatrix",
+        "createScriptProcessor",
+        "createSession",
+        "createShader",
+        "createShadowRoot",
+        "createStereoPanner",
+        "createStyleSheet",
+        "createTBody",
+        "createTFoot",
+        "createTHead",
+        "createTextNode",
+        "createTextRange",
+        "createTexture",
+        "createTouch",
+        "createTouchList",
+        "createTreeWalker",
+        "createWaveShaper",
+        "creationTime",
+        "crossOrigin",
+        "crypto",
+        "csi",
+        "cssFloat",
+        "cssRules",
+        "cssText",
+        "cssValueType",
+        "ctrlKey",
+        "ctrlLeft",
+        "cues",
+        "cullFace",
+        "currentNode",
+        "currentPage",
+        "currentScale",
+        "currentScript",
+        "currentSrc",
+        "currentState",
+        "currentStyle",
+        "currentTarget",
+        "currentTime",
+        "currentTranslate",
+        "currentView",
+        "cursor",
+        "curve",
+        "customError",
+        "cx",
+        "cy",
+        "d",
+        "data",
+        "dataFld",
+        "dataFormatAs",
+        "dataPageSize",
+        "dataSrc",
+        "dataTransfer",
+        "database",
+        "dataset",
+        "dateTime",
+        "db",
+        "debug",
+        "debuggerEnabled",
+        "declare",
+        "decode",
+        "decodeAudioData",
+        "decodeURI",
+        "decodeURIComponent",
+        "decrypt",
+        "default",
+        "defaultCharset",
+        "defaultChecked",
+        "defaultMuted",
+        "defaultPlaybackRate",
+        "defaultPrevented",
+        "defaultSelected",
+        "defaultStatus",
+        "defaultURL",
+        "defaultValue",
+        "defaultView",
+        "defaultstatus",
+        "defer",
+        "defineMagicFunction",
+        "defineMagicVariable",
+        "defineProperties",
+        "defineProperty",
+        "delayTime",
+        "delete",
+        "deleteBuffer",
+        "deleteCaption",
+        "deleteCell",
+        "deleteContents",
+        "deleteData",
+        "deleteDatabase",
+        "deleteFramebuffer",
+        "deleteFromDocument",
+        "deleteIndex",
+        "deleteMedium",
+        "deleteObjectStore",
+        "deleteProgram",
+        "deleteRenderbuffer",
+        "deleteRow",
+        "deleteRule",
+        "deleteShader",
+        "deleteTFoot",
+        "deleteTHead",
+        "deleteTexture",
+        "deliverChangeRecords",
+        "delivery",
+        "deliveryInfo",
+        "deliveryStatus",
+        "deliveryTimestamp",
+        "delta",
+        "deltaMode",
+        "deltaX",
+        "deltaY",
+        "deltaZ",
+        "depthFunc",
+        "depthMask",
+        "depthRange",
+        "deriveBits",
+        "deriveKey",
+        "description",
+        "deselectAll",
+        "designMode",
+        "destination",
+        "destinationURL",
+        "detach",
+        "detachEvent",
+        "detachShader",
+        "detail",
+        "detune",
+        "devicePixelRatio",
+        "deviceXDPI",
+        "deviceYDPI",
+        "diffuseConstant",
+        "digest",
+        "dimensions",
+        "dir",
+        "dirName",
+        "direction",
+        "dirxml",
+        "disable",
+        "disableVertexAttribArray",
+        "disabled",
+        "dischargingTime",
+        "disconnect",
+        "dispatchEvent",
+        "display",
+        "distanceModel",
+        "divisor",
+        "djsapi",
+        "djsproxy",
+        "doImport",
+        "doNotTrack",
+        "doScroll",
+        "doctype",
+        "document",
+        "documentElement",
+        "documentMode",
+        "documentURI",
+        "dolphin",
+        "dolphinGameCenter",
+        "dolphininfo",
+        "dolphinmeta",
+        "domComplete",
+        "domContentLoadedEventEnd",
+        "domContentLoadedEventStart",
+        "domInteractive",
+        "domLoading",
+        "domain",
+        "domainLookupEnd",
+        "domainLookupStart",
+        "dominant-baseline",
+        "dominantBaseline",
+        "done",
+        "dopplerFactor",
+        "download",
+        "dragDrop",
+        "draggable",
+        "drawArrays",
+        "drawArraysInstancedANGLE",
+        "drawCustomFocusRing",
+        "drawElements",
+        "drawElementsInstancedANGLE",
+        "drawFocusIfNeeded",
+        "drawImage",
+        "drawImageFromRect",
+        "drawSystemFocusRing",
+        "drawingBufferHeight",
+        "drawingBufferWidth",
+        "dropEffect",
+        "droppedVideoFrames",
+        "dropzone",
+        "dump",
+        "duplicate",
+        "duration",
+        "dvname",
+        "dvnum",
+        "dx",
+        "dy",
+        "dynsrc",
+        "e",
+        "edgeMode",
+        "effectAllowed",
+        "elapsedTime",
+        "elementFromPoint",
+        "elements",
+        "elevation",
+        "ellipse",
+        "email",
+        "embeds",
+        "empty",
+        "empty-cells",
+        "emptyCells",
+        "enable",
+        "enableBackground",
+        "enableStyleSheetsForSet",
+        "enableVertexAttribArray",
+        "enabled",
+        "enabledPlugin",
+        "encode",
+        "encodeURI",
+        "encodeURIComponent",
+        "encoding",
+        "encrypt",
+        "enctype",
+        "end",
+        "endContainer",
+        "endElement",
+        "endElementAt",
+        "endOfStream",
+        "endOffset",
+        "endTime",
+        "ended",
+        "endsWith",
+        "entities",
+        "entries",
+        "entryType",
+        "enumerate",
+        "enumerateEditable",
+        "error",
+        "errorCode",
+        "escape",
+        "eval",
+        "evaluate",
+        "event",
+        "eventPhase",
+        "every",
+        "exception",
+        "exec",
+        "execCommand",
+        "execCommandShowHelp",
+        "execScript",
+        "exitFullscreen",
+        "exitPointerLock",
+        "exp",
+        "expand",
+        "expandEntityReferences",
+        "expando",
+        "expansion",
+        "expiryDate",
+        "explicitOriginalTarget",
+        "expm1",
+        "exponent",
+        "exponentialRampToValueAtTime",
+        "exportKey",
+        "extend",
+        "extensions",
+        "extentNode",
+        "extentOffset",
+        "external",
+        "externalResourcesRequired",
+        "extractContents",
+        "extractable",
+        "f",
+        "face",
+        "factoryReset",
+        "fallback",
+        "familyName",
+        "farthestViewportElement",
+        "fastSeek",
+        "fatal",
+        "fetch",
+        "fetchStart",
+        "fftSize",
+        "fgColor",
+        "fileCreatedDate",
+        "fileHandle",
+        "fileModifiedDate",
+        "fileName",
+        "fileSize",
+        "fileUpdatedDate",
+        "filename",
+        "files",
+        "fill",
+        "fill-opacity",
+        "fill-rule",
+        "fillOpacity",
+        "fillRect",
+        "fillRule",
+        "fillStyle",
+        "fillText",
+        "filter",
+        "filterResX",
+        "filterResY",
+        "filterUnits",
+        "filters",
+        "find",
+        "findIndex",
+        "findRule",
+        "findText",
+        "finish",
+        "fireEvent",
+        "firstChild",
+        "firstElementChild",
+        "firstPage",
+        "fixed",
+        "flex",
+        "flex-basis",
+        "flex-direction",
+        "flex-flow",
+        "flex-grow",
+        "flex-shrink",
+        "flex-wrap",
+        "flexBasis",
+        "flexDirection",
+        "flexFlow",
+        "flexGrow",
+        "flexShrink",
+        "flexWrap",
+        "flipX",
+        "flipY",
+        "float",
+        "flood-color",
+        "flood-opacity",
+        "floodColor",
+        "floodOpacity",
+        "floor",
+        "flush",
+        "focus",
+        "focusNode",
+        "focusOffset",
+        "font",
+        "font-family",
+        "font-feature-settings",
+        "font-kerning",
+        "font-language-override",
+        "font-size",
+        "font-size-adjust",
+        "font-stretch",
+        "font-style",
+        "font-synthesis",
+        "font-variant",
+        "font-variant-alternates",
+        "font-variant-caps",
+        "font-variant-east-asian",
+        "font-variant-ligatures",
+        "font-variant-numeric",
+        "font-variant-position",
+        "font-weight",
+        "fontFamily",
+        "fontFeatureSettings",
+        "fontKerning",
+        "fontLanguageOverride",
+        "fontSize",
+        "fontSizeAdjust",
+        "fontSmoothingEnabled",
+        "fontStretch",
+        "fontStyle",
+        "fontSynthesis",
+        "fontVariant",
+        "fontVariantAlternates",
+        "fontVariantCaps",
+        "fontVariantEastAsian",
+        "fontVariantLigatures",
+        "fontVariantNumeric",
+        "fontVariantPosition",
+        "fontWeight",
+        "fontcolor",
+        "fonts",
+        "fontsize",
+        "for",
+        "forEach",
+        "forceRedraw",
+        "form",
+        "formAction",
+        "formEnctype",
+        "formMethod",
+        "formNoValidate",
+        "formTarget",
+        "format",
+        "forms",
+        "forward",
+        "fr",
+        "frame",
+        "frameBorder",
+        "frameElement",
+        "frameSpacing",
+        "framebufferRenderbuffer",
+        "framebufferTexture2D",
+        "frames",
+        "freeSpace",
+        "freeze",
+        "frequency",
+        "frequencyBinCount",
+        "from",
+        "fromCharCode",
+        "fromCodePoint",
+        "fromElement",
+        "frontFace",
+        "fround",
+        "fullScreen",
+        "fullscreenElement",
+        "fullscreenEnabled",
+        "fx",
+        "fy",
+        "gain",
+        "gamepad",
+        "gamma",
+        "genderIdentity",
+        "generateKey",
+        "generateMipmap",
+        "generateRequest",
+        "geolocation",
+        "gestureObject",
+        "get",
+        "getActiveAttrib",
+        "getActiveUniform",
+        "getAdjacentText",
+        "getAll",
+        "getAllResponseHeaders",
+        "getAsFile",
+        "getAsString",
+        "getAttachedShaders",
+        "getAttribLocation",
+        "getAttribute",
+        "getAttributeNS",
+        "getAttributeNode",
+        "getAttributeNodeNS",
+        "getAudioTracks",
+        "getBBox",
+        "getBattery",
+        "getBlob",
+        "getBookmark",
+        "getBoundingClientRect",
+        "getBufferParameter",
+        "getByteFrequencyData",
+        "getByteTimeDomainData",
+        "getCSSCanvasContext",
+        "getCTM",
+        "getCandidateWindowClientRect",
+        "getChannelData",
+        "getCharNumAtPosition",
+        "getClientRect",
+        "getClientRects",
+        "getCompositionAlternatives",
+        "getComputedStyle",
+        "getComputedTextLength",
+        "getConfiguration",
+        "getContext",
+        "getContextAttributes",
+        "getCounterValue",
+        "getCueAsHTML",
+        "getCueById",
+        "getCurrentPosition",
+        "getCurrentTime",
+        "getData",
+        "getDatabaseNames",
+        "getDate",
+        "getDay",
+        "getDefaultComputedStyle",
+        "getDestinationInsertionPoints",
+        "getDistributedNodes",
+        "getEditable",
+        "getElementById",
+        "getElementsByClassName",
+        "getElementsByName",
+        "getElementsByTagName",
+        "getElementsByTagNameNS",
+        "getEnclosureList",
+        "getEndPositionOfChar",
+        "getEntries",
+        "getEntriesByName",
+        "getEntriesByType",
+        "getError",
+        "getExtension",
+        "getExtentOfChar",
+        "getFeature",
+        "getFile",
+        "getFloat32",
+        "getFloat64",
+        "getFloatFrequencyData",
+        "getFloatTimeDomainData",
+        "getFloatValue",
+        "getFramebufferAttachmentParameter",
+        "getFrequencyResponse",
+        "getFullYear",
+        "getGamepads",
+        "getHours",
+        "getImageData",
+        "getInt16",
+        "getInt32",
+        "getInt8",
+        "getIntersectionList",
+        "getItem",
+        "getItems",
+        "getKey",
+        "getLineDash",
+        "getLocalStreams",
+        "getMarks",
+        "getMatchedCSSRules",
+        "getMeasures",
+        "getMetadata",
+        "getMilliseconds",
+        "getMinutes",
+        "getModifierState",
+        "getMonth",
+        "getNamedItem",
+        "getNamedItemNS",
+        "getNotifier",
+        "getNumberOfChars",
+        "getOverrideHistoryNavigationMode",
+        "getOverrideStyle",
+        "getOwnPropertyDescriptor",
+        "getOwnPropertyNames",
+        "getOwnPropertySymbols",
+        "getParameter",
+        "getPathSegAtLength",
+        "getPointAtLength",
+        "getPreference",
+        "getPreferenceDefault",
+        "getPresentationAttribute",
+        "getPreventDefault",
+        "getProgramInfoLog",
+        "getProgramParameter",
+        "getPropertyCSSValue",
+        "getPropertyPriority",
+        "getPropertyShorthand",
+        "getPropertyValue",
+        "getPrototypeOf",
+        "getRGBColorValue",
+        "getRandomValues",
+        "getRangeAt",
+        "getReceivers",
+        "getRectValue",
+        "getRegistration",
+        "getRemoteStreams",
+        "getRenderbufferParameter",
+        "getResponseHeader",
+        "getRoot",
+        "getRotationOfChar",
+        "getSVGDocument",
+        "getScreenCTM",
+        "getSeconds",
+        "getSelection",
+        "getSenders",
+        "getShaderInfoLog",
+        "getShaderParameter",
+        "getShaderPrecisionFormat",
+        "getShaderSource",
+        "getSimpleDuration",
+        "getSiteIcons",
+        "getSources",
+        "getSpeculativeParserUrls",
+        "getStartPositionOfChar",
+        "getStartTime",
+        "getStats",
+        "getStorageUpdates",
+        "getStreamById",
+        "getStringValue",
+        "getSubStringLength",
+        "getSubscription",
+        "getSupportedExtensions",
+        "getTexParameter",
+        "getTime",
+        "getTimezoneOffset",
+        "getTotalLength",
+        "getTrackById",
+        "getTracks",
+        "getTransformToElement",
+        "getUTCDate",
+        "getUTCDay",
+        "getUTCFullYear",
+        "getUTCHours",
+        "getUTCMilliseconds",
+        "getUTCMinutes",
+        "getUTCMonth",
+        "getUTCSeconds",
+        "getUint16",
+        "getUint32",
+        "getUint8",
+        "getUniform",
+        "getUniformLocation",
+        "getUserMedia",
+        "getValues",
+        "getVarDate",
+        "getVariableValue",
+        "getVertexAttrib",
+        "getVertexAttribOffset",
+        "getVideoPlaybackQuality",
+        "getVideoTracks",
+        "getWakeLockState",
+        "getYear",
+        "givenName",
+        "global",
+        "globalAlpha",
+        "globalCompositeOperation",
+        "glyphOrientationHorizontal",
+        "glyphOrientationVertical",
+        "glyphRef",
+        "go",
+        "gradientTransform",
+        "gradientUnits",
+        "grammars",
+        "green",
+        "group",
+        "groupCollapsed",
+        "groupEnd",
+        "hardwareConcurrency",
+        "has",
+        "hasAttribute",
+        "hasAttributeNS",
+        "hasAttributes",
+        "hasChildNodes",
+        "hasComposition",
+        "hasExtension",
+        "hasFeature",
+        "hasFocus",
+        "hasLayout",
+        "hasOwnProperty",
+        "hash",
+        "head",
+        "headers",
+        "heading",
+        "height",
+        "hidden",
+        "hide",
+        "hideFocus",
+        "high",
+        "hint",
+        "history",
+        "honorificPrefix",
+        "honorificSuffix",
+        "horizontalOverflow",
+        "host",
+        "hostname",
+        "href",
+        "hreflang",
+        "hspace",
+        "html5TagCheckInerface",
+        "htmlFor",
+        "htmlText",
+        "httpEquiv",
+        "hwTimestamp",
+        "hypot",
+        "iccId",
+        "iceConnectionState",
+        "iceGatheringState",
+        "icon",
+        "id",
+        "identifier",
+        "identity",
+        "ignoreBOM",
+        "ignoreCase",
+        "image-orientation",
+        "image-rendering",
+        "imageOrientation",
+        "imageRendering",
+        "images",
+        "ime-mode",
+        "imeMode",
+        "implementation",
+        "importKey",
+        "importNode",
+        "importStylesheet",
+        "imports",
+        "impp",
+        "imul",
+        "in1",
+        "in2",
+        "inBandMetadataTrackDispatchType",
+        "inRange",
+        "includes",
+        "incremental",
+        "indeterminate",
+        "index",
+        "indexNames",
+        "indexOf",
+        "indexedDB",
+        "inertiaDestinationX",
+        "inertiaDestinationY",
+        "info",
+        "init",
+        "initAnimationEvent",
+        "initBeforeLoadEvent",
+        "initClipboardEvent",
+        "initCloseEvent",
+        "initCommandEvent",
+        "initCompositionEvent",
+        "initCustomEvent",
+        "initData",
+        "initDeviceMotionEvent",
+        "initDeviceOrientationEvent",
+        "initDragEvent",
+        "initErrorEvent",
+        "initEvent",
+        "initFocusEvent",
+        "initGestureEvent",
+        "initHashChangeEvent",
+        "initKeyEvent",
+        "initKeyboardEvent",
+        "initMSManipulationEvent",
+        "initMessageEvent",
+        "initMouseEvent",
+        "initMouseScrollEvent",
+        "initMouseWheelEvent",
+        "initMutationEvent",
+        "initNSMouseEvent",
+        "initOverflowEvent",
+        "initPageEvent",
+        "initPageTransitionEvent",
+        "initPointerEvent",
+        "initPopStateEvent",
+        "initProgressEvent",
+        "initScrollAreaEvent",
+        "initSimpleGestureEvent",
+        "initStorageEvent",
+        "initTextEvent",
+        "initTimeEvent",
+        "initTouchEvent",
+        "initTransitionEvent",
+        "initUIEvent",
+        "initWebKitAnimationEvent",
+        "initWebKitTransitionEvent",
+        "initWebKitWheelEvent",
+        "initWheelEvent",
+        "initialTime",
+        "initialize",
+        "initiatorType",
+        "inner",
+        "innerHTML",
+        "innerHeight",
+        "innerText",
+        "innerWidth",
+        "input",
+        "inputBuffer",
+        "inputEncoding",
+        "inputMethod",
+        "insertAdjacentElement",
+        "insertAdjacentHTML",
+        "insertAdjacentText",
+        "insertBefore",
+        "insertCell",
+        "insertData",
+        "insertItemBefore",
+        "insertNode",
+        "insertRow",
+        "insertRule",
+        "instanceRoot",
+        "intercept",
+        "interimResults",
+        "internalSubset",
+        "intersectsNode",
+        "interval",
+        "invalidIteratorState",
+        "inverse",
+        "invertSelf",
+        "is",
+        "is2D",
+        "isAlternate",
+        "isArray",
+        "isBingCurrentSearchDefault",
+        "isBuffer",
+        "isCandidateWindowVisible",
+        "isChar",
+        "isCollapsed",
+        "isComposing",
+        "isContentEditable",
+        "isContentHandlerRegistered",
+        "isContextLost",
+        "isDefaultNamespace",
+        "isDisabled",
+        "isEnabled",
+        "isEqual",
+        "isEqualNode",
+        "isExtensible",
+        "isFinite",
+        "isFramebuffer",
+        "isFrozen",
+        "isGenerator",
+        "isId",
+        "isInjected",
+        "isInteger",
+        "isMap",
+        "isMultiLine",
+        "isNaN",
+        "isOpen",
+        "isPointInFill",
+        "isPointInPath",
+        "isPointInRange",
+        "isPointInStroke",
+        "isPrefAlternate",
+        "isPrimary",
+        "isProgram",
+        "isPropertyImplicit",
+        "isProtocolHandlerRegistered",
+        "isPrototypeOf",
+        "isRenderbuffer",
+        "isSafeInteger",
+        "isSameNode",
+        "isSealed",
+        "isShader",
+        "isSupported",
+        "isTextEdit",
+        "isTexture",
+        "isTrusted",
+        "isTypeSupported",
+        "isView",
+        "isolation",
+        "italics",
+        "item",
+        "itemId",
+        "itemProp",
+        "itemRef",
+        "itemScope",
+        "itemType",
+        "itemValue",
+        "iterateNext",
+        "iterator",
+        "javaEnabled",
+        "jobTitle",
+        "join",
+        "json",
+        "justify-content",
+        "justifyContent",
+        "k1",
+        "k2",
+        "k3",
+        "k4",
+        "kernelMatrix",
+        "kernelUnitLengthX",
+        "kernelUnitLengthY",
+        "kerning",
+        "key",
+        "keyCode",
+        "keyFor",
+        "keyIdentifier",
+        "keyLightEnabled",
+        "keyLocation",
+        "keyPath",
+        "keySystem",
+        "keyText",
+        "keyUsage",
+        "keys",
+        "keytype",
+        "kind",
+        "knee",
+        "label",
+        "labels",
+        "lang",
+        "language",
+        "languages",
+        "largeArcFlag",
+        "lastChild",
+        "lastElementChild",
+        "lastEventId",
+        "lastIndex",
+        "lastIndexOf",
+        "lastMatch",
+        "lastMessageSubject",
+        "lastMessageType",
+        "lastModified",
+        "lastModifiedDate",
+        "lastPage",
+        "lastParen",
+        "lastState",
+        "lastStyleSheetSet",
+        "latitude",
+        "layerX",
+        "layerY",
+        "layoutFlow",
+        "layoutGrid",
+        "layoutGridChar",
+        "layoutGridLine",
+        "layoutGridMode",
+        "layoutGridType",
+        "lbound",
+        "left",
+        "leftContext",
+        "leftMargin",
+        "length",
+        "lengthAdjust",
+        "lengthComputable",
+        "letter-spacing",
+        "letterSpacing",
+        "level",
+        "lighting-color",
+        "lightingColor",
+        "limitingConeAngle",
+        "line",
+        "line-height",
+        "lineAlign",
+        "lineBreak",
+        "lineCap",
+        "lineDashOffset",
+        "lineHeight",
+        "lineJoin",
+        "lineNumber",
+        "lineTo",
+        "lineWidth",
+        "linearRampToValueAtTime",
+        "lineno",
+        "link",
+        "linkColor",
+        "linkProgram",
+        "links",
+        "list",
+        "list-style",
+        "list-style-image",
+        "list-style-position",
+        "list-style-type",
+        "listStyle",
+        "listStyleImage",
+        "listStylePosition",
+        "listStyleType",
+        "listener",
+        "load",
+        "loadEventEnd",
+        "loadEventStart",
+        "loadTimes",
+        "loaded",
+        "localDescription",
+        "localName",
+        "localStorage",
+        "locale",
+        "localeCompare",
+        "location",
+        "locationbar",
+        "lock",
+        "lockedFile",
+        "log",
+        "log10",
+        "log1p",
+        "log2",
+        "logicalXDPI",
+        "logicalYDPI",
+        "longDesc",
+        "longitude",
+        "lookupNamespaceURI",
+        "lookupPrefix",
+        "loop",
+        "loopEnd",
+        "loopStart",
+        "looping",
+        "low",
+        "lower",
+        "lowerBound",
+        "lowerOpen",
+        "lowsrc",
+        "m11",
+        "m12",
+        "m13",
+        "m14",
+        "m21",
+        "m22",
+        "m23",
+        "m24",
+        "m31",
+        "m32",
+        "m33",
+        "m34",
+        "m41",
+        "m42",
+        "m43",
+        "m44",
+        "manifest",
+        "map",
+        "mapping",
+        "margin",
+        "margin-bottom",
+        "margin-left",
+        "margin-right",
+        "margin-top",
+        "marginBottom",
+        "marginHeight",
+        "marginLeft",
+        "marginRight",
+        "marginTop",
+        "marginWidth",
+        "mark",
+        "marker",
+        "marker-end",
+        "marker-mid",
+        "marker-offset",
+        "marker-start",
+        "markerEnd",
+        "markerHeight",
+        "markerMid",
+        "markerOffset",
+        "markerStart",
+        "markerUnits",
+        "markerWidth",
+        "marks",
+        "mask",
+        "mask-type",
+        "maskContentUnits",
+        "maskType",
+        "maskUnits",
+        "match",
+        "matchMedia",
+        "matchMedium",
+        "matches",
+        "matrix",
+        "matrixTransform",
+        "max",
+        "max-height",
+        "max-width",
+        "maxAlternatives",
+        "maxChannelCount",
+        "maxConnectionsPerServer",
+        "maxDecibels",
+        "maxDistance",
+        "maxHeight",
+        "maxLength",
+        "maxTouchPoints",
+        "maxValue",
+        "maxWidth",
+        "measure",
+        "measureText",
+        "media",
+        "mediaDevices",
+        "mediaElement",
+        "mediaGroup",
+        "mediaKeys",
+        "mediaText",
+        "meetOrSlice",
+        "memory",
+        "menubar",
+        "mergeAttributes",
+        "message",
+        "messageClass",
+        "messageHandlers",
+        "metaKey",
+        "method",
+        "mimeType",
+        "mimeTypes",
+        "min",
+        "min-height",
+        "min-width",
+        "minDecibels",
+        "minHeight",
+        "minValue",
+        "minWidth",
+        "miterLimit",
+        "mix-blend-mode",
+        "mixBlendMode",
+        "mode",
+        "modify",
+        "mount",
+        "move",
+        "moveBy",
+        "moveEnd",
+        "moveFirst",
+        "moveFocusDown",
+        "moveFocusLeft",
+        "moveFocusRight",
+        "moveFocusUp",
+        "moveNext",
+        "moveRow",
+        "moveStart",
+        "moveTo",
+        "moveToBookmark",
+        "moveToElementText",
+        "moveToPoint",
+        "mozAdd",
+        "mozAnimationStartTime",
+        "mozAnon",
+        "mozApps",
+        "mozAudioCaptured",
+        "mozAudioChannelType",
+        "mozAutoplayEnabled",
+        "mozCancelAnimationFrame",
+        "mozCancelFullScreen",
+        "mozCancelRequestAnimationFrame",
+        "mozCaptureStream",
+        "mozCaptureStreamUntilEnded",
+        "mozClearDataAt",
+        "mozContact",
+        "mozContacts",
+        "mozCreateFileHandle",
+        "mozCurrentTransform",
+        "mozCurrentTransformInverse",
+        "mozCursor",
+        "mozDash",
+        "mozDashOffset",
+        "mozDecodedFrames",
+        "mozExitPointerLock",
+        "mozFillRule",
+        "mozFragmentEnd",
+        "mozFrameDelay",
+        "mozFullScreen",
+        "mozFullScreenElement",
+        "mozFullScreenEnabled",
+        "mozGetAll",
+        "mozGetAllKeys",
+        "mozGetAsFile",
+        "mozGetDataAt",
+        "mozGetMetadata",
+        "mozGetUserMedia",
+        "mozHasAudio",
+        "mozHasItem",
+        "mozHidden",
+        "mozImageSmoothingEnabled",
+        "mozIndexedDB",
+        "mozInnerScreenX",
+        "mozInnerScreenY",
+        "mozInputSource",
+        "mozIsTextField",
+        "mozItem",
+        "mozItemCount",
+        "mozItems",
+        "mozLength",
+        "mozLockOrientation",
+        "mozMatchesSelector",
+        "mozMovementX",
+        "mozMovementY",
+        "mozOpaque",
+        "mozOrientation",
+        "mozPaintCount",
+        "mozPaintedFrames",
+        "mozParsedFrames",
+        "mozPay",
+        "mozPointerLockElement",
+        "mozPresentedFrames",
+        "mozPreservesPitch",
+        "mozPressure",
+        "mozPrintCallback",
+        "mozRTCIceCandidate",
+        "mozRTCPeerConnection",
+        "mozRTCSessionDescription",
+        "mozRemove",
+        "mozRequestAnimationFrame",
+        "mozRequestFullScreen",
+        "mozRequestPointerLock",
+        "mozSetDataAt",
+        "mozSetImageElement",
+        "mozSourceNode",
+        "mozSrcObject",
+        "mozSystem",
+        "mozTCPSocket",
+        "mozTextStyle",
+        "mozTypesAt",
+        "mozUnlockOrientation",
+        "mozUserCancelled",
+        "mozVisibilityState",
+        "msAnimation",
+        "msAnimationDelay",
+        "msAnimationDirection",
+        "msAnimationDuration",
+        "msAnimationFillMode",
+        "msAnimationIterationCount",
+        "msAnimationName",
+        "msAnimationPlayState",
+        "msAnimationStartTime",
+        "msAnimationTimingFunction",
+        "msBackfaceVisibility",
+        "msBlockProgression",
+        "msCSSOMElementFloatMetrics",
+        "msCaching",
+        "msCachingEnabled",
+        "msCancelRequestAnimationFrame",
+        "msCapsLockWarningOff",
+        "msClearImmediate",
+        "msClose",
+        "msContentZoomChaining",
+        "msContentZoomFactor",
+        "msContentZoomLimit",
+        "msContentZoomLimitMax",
+        "msContentZoomLimitMin",
+        "msContentZoomSnap",
+        "msContentZoomSnapPoints",
+        "msContentZoomSnapType",
+        "msContentZooming",
+        "msConvertURL",
+        "msCrypto",
+        "msDoNotTrack",
+        "msElementsFromPoint",
+        "msElementsFromRect",
+        "msExitFullscreen",
+        "msExtendedCode",
+        "msFillRule",
+        "msFirstPaint",
+        "msFlex",
+        "msFlexAlign",
+        "msFlexDirection",
+        "msFlexFlow",
+        "msFlexItemAlign",
+        "msFlexLinePack",
+        "msFlexNegative",
+        "msFlexOrder",
+        "msFlexPack",
+        "msFlexPositive",
+        "msFlexPreferredSize",
+        "msFlexWrap",
+        "msFlowFrom",
+        "msFlowInto",
+        "msFontFeatureSettings",
+        "msFullscreenElement",
+        "msFullscreenEnabled",
+        "msGetInputContext",
+        "msGetRegionContent",
+        "msGetUntransformedBounds",
+        "msGraphicsTrustStatus",
+        "msGridColumn",
+        "msGridColumnAlign",
+        "msGridColumnSpan",
+        "msGridColumns",
+        "msGridRow",
+        "msGridRowAlign",
+        "msGridRowSpan",
+        "msGridRows",
+        "msHidden",
+        "msHighContrastAdjust",
+        "msHyphenateLimitChars",
+        "msHyphenateLimitLines",
+        "msHyphenateLimitZone",
+        "msHyphens",
+        "msImageSmoothingEnabled",
+        "msImeAlign",
+        "msIndexedDB",
+        "msInterpolationMode",
+        "msIsStaticHTML",
+        "msKeySystem",
+        "msKeys",
+        "msLaunchUri",
+        "msLockOrientation",
+        "msManipulationViewsEnabled",
+        "msMatchMedia",
+        "msMatchesSelector",
+        "msMaxTouchPoints",
+        "msOrientation",
+        "msOverflowStyle",
+        "msPerspective",
+        "msPerspectiveOrigin",
+        "msPlayToDisabled",
+        "msPlayToPreferredSourceUri",
+        "msPlayToPrimary",
+        "msPointerEnabled",
+        "msRegionOverflow",
+        "msReleasePointerCapture",
+        "msRequestAnimationFrame",
+        "msRequestFullscreen",
+        "msSaveBlob",
+        "msSaveOrOpenBlob",
+        "msScrollChaining",
+        "msScrollLimit",
+        "msScrollLimitXMax",
+        "msScrollLimitXMin",
+        "msScrollLimitYMax",
+        "msScrollLimitYMin",
+        "msScrollRails",
+        "msScrollSnapPointsX",
+        "msScrollSnapPointsY",
+        "msScrollSnapType",
+        "msScrollSnapX",
+        "msScrollSnapY",
+        "msScrollTranslation",
+        "msSetImmediate",
+        "msSetMediaKeys",
+        "msSetPointerCapture",
+        "msTextCombineHorizontal",
+        "msTextSizeAdjust",
+        "msToBlob",
+        "msTouchAction",
+        "msTouchSelect",
+        "msTraceAsyncCallbackCompleted",
+        "msTraceAsyncCallbackStarting",
+        "msTraceAsyncOperationCompleted",
+        "msTraceAsyncOperationStarting",
+        "msTransform",
+        "msTransformOrigin",
+        "msTransformStyle",
+        "msTransition",
+        "msTransitionDelay",
+        "msTransitionDuration",
+        "msTransitionProperty",
+        "msTransitionTimingFunction",
+        "msUnlockOrientation",
+        "msUpdateAsyncCallbackRelation",
+        "msUserSelect",
+        "msVisibilityState",
+        "msWrapFlow",
+        "msWrapMargin",
+        "msWrapThrough",
+        "msWriteProfilerMark",
+        "msZoom",
+        "msZoomTo",
+        "mt",
+        "multiEntry",
+        "multiSelectionObj",
+        "multiline",
+        "multiple",
+        "multiply",
+        "multiplySelf",
+        "mutableFile",
+        "muted",
+        "n",
+        "name",
+        "nameProp",
+        "namedItem",
+        "namedRecordset",
+        "names",
+        "namespaceURI",
+        "namespaces",
+        "naturalHeight",
+        "naturalWidth",
+        "navigate",
+        "navigation",
+        "navigationMode",
+        "navigationStart",
+        "navigator",
+        "near",
+        "nearestViewportElement",
+        "negative",
+        "netscape",
+        "networkState",
+        "newScale",
+        "newTranslate",
+        "newURL",
+        "newValue",
+        "newValueSpecifiedUnits",
+        "newVersion",
+        "newhome",
+        "next",
+        "nextElementSibling",
+        "nextNode",
+        "nextPage",
+        "nextSibling",
+        "nickname",
+        "noHref",
+        "noResize",
+        "noShade",
+        "noValidate",
+        "noWrap",
+        "nodeName",
+        "nodeType",
+        "nodeValue",
+        "normalize",
+        "normalizedPathSegList",
+        "notationName",
+        "notations",
+        "note",
+        "noteGrainOn",
+        "noteOff",
+        "noteOn",
+        "now",
+        "numOctaves",
+        "number",
+        "numberOfChannels",
+        "numberOfInputs",
+        "numberOfItems",
+        "numberOfOutputs",
+        "numberValue",
+        "oMatchesSelector",
+        "object",
+        "object-fit",
+        "object-position",
+        "objectFit",
+        "objectPosition",
+        "objectStore",
+        "objectStoreNames",
+        "observe",
+        "of",
+        "offscreenBuffering",
+        "offset",
+        "offsetHeight",
+        "offsetLeft",
+        "offsetNode",
+        "offsetParent",
+        "offsetTop",
+        "offsetWidth",
+        "offsetX",
+        "offsetY",
+        "ok",
+        "oldURL",
+        "oldValue",
+        "oldVersion",
+        "olderShadowRoot",
+        "onLine",
+        "onabort",
+        "onactivate",
+        "onactive",
+        "onaddstream",
+        "onaddtrack",
+        "onafterprint",
+        "onafterscriptexecute",
+        "onafterupdate",
+        "onaudioend",
+        "onaudioprocess",
+        "onaudiostart",
+        "onautocomplete",
+        "onautocompleteerror",
+        "onbeforeactivate",
+        "onbeforecopy",
+        "onbeforecut",
+        "onbeforedeactivate",
+        "onbeforeeditfocus",
+        "onbeforepaste",
+        "onbeforeprint",
+        "onbeforescriptexecute",
+        "onbeforeunload",
+        "onbeforeupdate",
+        "onblocked",
+        "onblur",
+        "onbounce",
+        "onboundary",
+        "oncached",
+        "oncancel",
+        "oncandidatewindowhide",
+        "oncandidatewindowshow",
+        "oncandidatewindowupdate",
+        "oncanplay",
+        "oncanplaythrough",
+        "oncellchange",
+        "onchange",
+        "onchargingchange",
+        "onchargingtimechange",
+        "onchecking",
+        "onclick",
+        "onclose",
+        "oncompassneedscalibration",
+        "oncomplete",
+        "oncontextmenu",
+        "oncontrolselect",
+        "oncopy",
+        "oncuechange",
+        "oncut",
+        "ondataavailable",
+        "ondatachannel",
+        "ondatasetchanged",
+        "ondatasetcomplete",
+        "ondblclick",
+        "ondeactivate",
+        "ondevicelight",
+        "ondevicemotion",
+        "ondeviceorientation",
+        "ondeviceproximity",
+        "ondischargingtimechange",
+        "ondisplay",
+        "ondownloading",
+        "ondrag",
+        "ondragend",
+        "ondragenter",
+        "ondragleave",
+        "ondragover",
+        "ondragstart",
+        "ondrop",
+        "ondurationchange",
+        "onemptied",
+        "onencrypted",
+        "onend",
+        "onended",
+        "onenter",
+        "onerror",
+        "onerrorupdate",
+        "onexit",
+        "onfilterchange",
+        "onfinish",
+        "onfocus",
+        "onfocusin",
+        "onfocusout",
+        "onfullscreenchange",
+        "onfullscreenerror",
+        "ongesturechange",
+        "ongestureend",
+        "ongesturestart",
+        "ongotpointercapture",
+        "onhashchange",
+        "onhelp",
+        "onicecandidate",
+        "oniceconnectionstatechange",
+        "oninactive",
+        "oninput",
+        "oninvalid",
+        "onkeydown",
+        "onkeypress",
+        "onkeyup",
+        "onlanguagechange",
+        "onlayoutcomplete",
+        "onlevelchange",
+        "onload",
+        "onloadeddata",
+        "onloadedmetadata",
+        "onloadend",
+        "onloadstart",
+        "onlosecapture",
+        "onlostpointercapture",
+        "only",
+        "onmark",
+        "onmessage",
+        "onmousedown",
+        "onmouseenter",
+        "onmouseleave",
+        "onmousemove",
+        "onmouseout",
+        "onmouseover",
+        "onmouseup",
+        "onmousewheel",
+        "onmove",
+        "onmoveend",
+        "onmovestart",
+        "onmozfullscreenchange",
+        "onmozfullscreenerror",
+        "onmozorientationchange",
+        "onmozpointerlockchange",
+        "onmozpointerlockerror",
+        "onmscontentzoom",
+        "onmsfullscreenchange",
+        "onmsfullscreenerror",
+        "onmsgesturechange",
+        "onmsgesturedoubletap",
+        "onmsgestureend",
+        "onmsgesturehold",
+        "onmsgesturestart",
+        "onmsgesturetap",
+        "onmsgotpointercapture",
+        "onmsinertiastart",
+        "onmslostpointercapture",
+        "onmsmanipulationstatechanged",
+        "onmsneedkey",
+        "onmsorientationchange",
+        "onmspointercancel",
+        "onmspointerdown",
+        "onmspointerenter",
+        "onmspointerhover",
+        "onmspointerleave",
+        "onmspointermove",
+        "onmspointerout",
+        "onmspointerover",
+        "onmspointerup",
+        "onmssitemodejumplistitemremoved",
+        "onmsthumbnailclick",
+        "onnegotiationneeded",
+        "onnomatch",
+        "onnoupdate",
+        "onobsolete",
+        "onoffline",
+        "ononline",
+        "onopen",
+        "onorientationchange",
+        "onpagechange",
+        "onpagehide",
+        "onpageshow",
+        "onpaste",
+        "onpause",
+        "onplay",
+        "onplaying",
+        "onpluginstreamstart",
+        "onpointercancel",
+        "onpointerdown",
+        "onpointerenter",
+        "onpointerleave",
+        "onpointerlockchange",
+        "onpointerlockerror",
+        "onpointermove",
+        "onpointerout",
+        "onpointerover",
+        "onpointerup",
+        "onpopstate",
+        "onprogress",
+        "onpropertychange",
+        "onratechange",
+        "onreadystatechange",
+        "onremovestream",
+        "onremovetrack",
+        "onreset",
+        "onresize",
+        "onresizeend",
+        "onresizestart",
+        "onresourcetimingbufferfull",
+        "onresult",
+        "onresume",
+        "onrowenter",
+        "onrowexit",
+        "onrowsdelete",
+        "onrowsinserted",
+        "onscroll",
+        "onsearch",
+        "onseeked",
+        "onseeking",
+        "onselect",
+        "onselectionchange",
+        "onselectstart",
+        "onshow",
+        "onsignalingstatechange",
+        "onsoundend",
+        "onsoundstart",
+        "onspeechend",
+        "onspeechstart",
+        "onstalled",
+        "onstart",
+        "onstatechange",
+        "onstop",
+        "onstorage",
+        "onstoragecommit",
+        "onsubmit",
+        "onsuccess",
+        "onsuspend",
+        "ontextinput",
+        "ontimeout",
+        "ontimeupdate",
+        "ontoggle",
+        "ontouchcancel",
+        "ontouchend",
+        "ontouchmove",
+        "ontouchstart",
+        "ontransitionend",
+        "onunload",
+        "onupdateready",
+        "onupgradeneeded",
+        "onuserproximity",
+        "onversionchange",
+        "onvoiceschanged",
+        "onvolumechange",
+        "onwaiting",
+        "onwarning",
+        "onwebkitanimationend",
+        "onwebkitanimationiteration",
+        "onwebkitanimationstart",
+        "onwebkitcurrentplaybacktargetiswirelesschanged",
+        "onwebkitfullscreenchange",
+        "onwebkitfullscreenerror",
+        "onwebkitkeyadded",
+        "onwebkitkeyerror",
+        "onwebkitkeymessage",
+        "onwebkitneedkey",
+        "onwebkitorientationchange",
+        "onwebkitplaybacktargetavailabilitychanged",
+        "onwebkitpointerlockchange",
+        "onwebkitpointerlockerror",
+        "onwebkitresourcetimingbufferfull",
+        "onwebkittransitionend",
+        "onwheel",
+        "onzoom",
+        "opacity",
+        "open",
+        "openCursor",
+        "openDatabase",
+        "openKeyCursor",
+        "opener",
+        "opera",
+        "operationType",
+        "operator",
+        "opr",
+        "optimum",
+        "options",
+        "order",
+        "orderX",
+        "orderY",
+        "ordered",
+        "org",
+        "orient",
+        "orientAngle",
+        "orientType",
+        "orientation",
+        "origin",
+        "originalTarget",
+        "orphans",
+        "oscpu",
+        "outerHTML",
+        "outerHeight",
+        "outerText",
+        "outerWidth",
+        "outline",
+        "outline-color",
+        "outline-offset",
+        "outline-style",
+        "outline-width",
+        "outlineColor",
+        "outlineOffset",
+        "outlineStyle",
+        "outlineWidth",
+        "outputBuffer",
+        "overflow",
+        "overflow-x",
+        "overflow-y",
+        "overflowX",
+        "overflowY",
+        "overrideMimeType",
+        "oversample",
+        "ownerDocument",
+        "ownerElement",
+        "ownerNode",
+        "ownerRule",
+        "ownerSVGElement",
+        "owningElement",
+        "p1",
+        "p2",
+        "p3",
+        "p4",
+        "pad",
+        "padding",
+        "padding-bottom",
+        "padding-left",
+        "padding-right",
+        "padding-top",
+        "paddingBottom",
+        "paddingLeft",
+        "paddingRight",
+        "paddingTop",
+        "page",
+        "page-break-after",
+        "page-break-before",
+        "page-break-inside",
+        "pageBreakAfter",
+        "pageBreakBefore",
+        "pageBreakInside",
+        "pageCount",
+        "pageX",
+        "pageXOffset",
+        "pageY",
+        "pageYOffset",
+        "pages",
+        "paint-order",
+        "paintOrder",
+        "paintRequests",
+        "paintType",
+        "palette",
+        "panningModel",
+        "parent",
+        "parentElement",
+        "parentNode",
+        "parentRule",
+        "parentStyleSheet",
+        "parentTextEdit",
+        "parentWindow",
+        "parse",
+        "parseFloat",
+        "parseFromString",
+        "parseInt",
+        "participants",
+        "password",
+        "pasteHTML",
+        "path",
+        "pathLength",
+        "pathSegList",
+        "pathSegType",
+        "pathSegTypeAsLetter",
+        "pathname",
+        "pattern",
+        "patternContentUnits",
+        "patternMismatch",
+        "patternTransform",
+        "patternUnits",
+        "pause",
+        "pauseAnimations",
+        "pauseOnExit",
+        "paused",
+        "pending",
+        "performance",
+        "permission",
+        "persisted",
+        "personalbar",
+        "perspective",
+        "perspective-origin",
+        "perspectiveOrigin",
+        "phoneticFamilyName",
+        "phoneticGivenName",
+        "photo",
+        "ping",
+        "pitch",
+        "pixelBottom",
+        "pixelDepth",
+        "pixelHeight",
+        "pixelLeft",
+        "pixelRight",
+        "pixelStorei",
+        "pixelTop",
+        "pixelUnitToMillimeterX",
+        "pixelUnitToMillimeterY",
+        "pixelWidth",
+        "placeholder",
+        "platform",
+        "play",
+        "playbackRate",
+        "playbackState",
+        "playbackTime",
+        "played",
+        "plugins",
+        "pluginspage",
+        "pname",
+        "pointer-events",
+        "pointerBeforeReferenceNode",
+        "pointerEnabled",
+        "pointerEvents",
+        "pointerId",
+        "pointerLockElement",
+        "pointerType",
+        "points",
+        "pointsAtX",
+        "pointsAtY",
+        "pointsAtZ",
+        "polygonOffset",
+        "pop",
+        "popupWindowFeatures",
+        "popupWindowName",
+        "popupWindowURI",
+        "port",
+        "port1",
+        "port2",
+        "ports",
+        "posBottom",
+        "posHeight",
+        "posLeft",
+        "posRight",
+        "posTop",
+        "posWidth",
+        "position",
+        "positionAlign",
+        "postError",
+        "postMessage",
+        "poster",
+        "pow",
+        "powerOff",
+        "preMultiplySelf",
+        "precision",
+        "preferredStyleSheetSet",
+        "preferredStylesheetSet",
+        "prefix",
+        "preload",
+        "preserveAlpha",
+        "preserveAspectRatio",
+        "preserveAspectRatioString",
+        "pressed",
+        "pressure",
+        "prevValue",
+        "preventDefault",
+        "preventExtensions",
+        "previousElementSibling",
+        "previousNode",
+        "previousPage",
+        "previousScale",
+        "previousSibling",
+        "previousTranslate",
+        "primaryKey",
+        "primitiveType",
+        "primitiveUnits",
+        "principals",
+        "print",
+        "privateKey",
+        "probablySupportsContext",
+        "process",
+        "processIceMessage",
+        "product",
+        "productSub",
+        "profile",
+        "profileEnd",
+        "profiles",
+        "prompt",
+        "properties",
+        "propertyIsEnumerable",
+        "propertyName",
+        "protocol",
+        "protocolLong",
+        "prototype",
+        "pseudoClass",
+        "pseudoElement",
+        "publicId",
+        "publicKey",
+        "published",
+        "push",
+        "pushNotification",
+        "pushState",
+        "put",
+        "putImageData",
+        "quadraticCurveTo",
+        "qualifier",
+        "queryCommandEnabled",
+        "queryCommandIndeterm",
+        "queryCommandState",
+        "queryCommandSupported",
+        "queryCommandText",
+        "queryCommandValue",
+        "querySelector",
+        "querySelectorAll",
+        "quote",
+        "quotes",
+        "r",
+        "r1",
+        "r2",
+        "race",
+        "radiogroup",
+        "radiusX",
+        "radiusY",
+        "random",
+        "range",
+        "rangeCount",
+        "rangeMax",
+        "rangeMin",
+        "rangeOffset",
+        "rangeOverflow",
+        "rangeParent",
+        "rangeUnderflow",
+        "rate",
+        "ratio",
+        "raw",
+        "read",
+        "readAsArrayBuffer",
+        "readAsBinaryString",
+        "readAsBlob",
+        "readAsDataURL",
+        "readAsText",
+        "readOnly",
+        "readPixels",
+        "readReportRequested",
+        "readyState",
+        "reason",
+        "reboot",
+        "receiver",
+        "receivers",
+        "recordNumber",
+        "recordset",
+        "rect",
+        "red",
+        "redirectCount",
+        "redirectEnd",
+        "redirectStart",
+        "reduce",
+        "reduceRight",
+        "reduction",
+        "refDistance",
+        "refX",
+        "refY",
+        "referenceNode",
+        "referrer",
+        "refresh",
+        "region",
+        "regionAnchorX",
+        "regionAnchorY",
+        "regionId",
+        "regions",
+        "register",
+        "registerContentHandler",
+        "registerElement",
+        "registerProtocolHandler",
+        "reject",
+        "rel",
+        "relList",
+        "relatedNode",
+        "relatedTarget",
+        "release",
+        "releaseCapture",
+        "releaseEvents",
+        "releasePointerCapture",
+        "releaseShaderCompiler",
+        "reliable",
+        "reload",
+        "remainingSpace",
+        "remoteDescription",
+        "remove",
+        "removeAllRanges",
+        "removeAttribute",
+        "removeAttributeNS",
+        "removeAttributeNode",
+        "removeBehavior",
+        "removeChild",
+        "removeCue",
+        "removeEventListener",
+        "removeFilter",
+        "removeImport",
+        "removeItem",
+        "removeListener",
+        "removeNamedItem",
+        "removeNamedItemNS",
+        "removeNode",
+        "removeParameter",
+        "removeProperty",
+        "removeRange",
+        "removeRegion",
+        "removeRule",
+        "removeSiteSpecificTrackingException",
+        "removeSourceBuffer",
+        "removeStream",
+        "removeTrack",
+        "removeVariable",
+        "removeWakeLockListener",
+        "removeWebWideTrackingException",
+        "removedNodes",
+        "renderbufferStorage",
+        "renderedBuffer",
+        "renderingMode",
+        "repeat",
+        "replace",
+        "replaceAdjacentText",
+        "replaceChild",
+        "replaceData",
+        "replaceId",
+        "replaceItem",
+        "replaceNode",
+        "replaceState",
+        "replaceTrack",
+        "replaceWholeText",
+        "reportValidity",
+        "requestAnimationFrame",
+        "requestAutocomplete",
+        "requestData",
+        "requestFullscreen",
+        "requestMediaKeySystemAccess",
+        "requestPermission",
+        "requestPointerLock",
+        "requestStart",
+        "requestingWindow",
+        "required",
+        "requiredExtensions",
+        "requiredFeatures",
+        "reset",
+        "resetTransform",
+        "resize",
+        "resizeBy",
+        "resizeTo",
+        "resolve",
+        "response",
+        "responseBody",
+        "responseEnd",
+        "responseStart",
+        "responseText",
+        "responseType",
+        "responseURL",
+        "responseXML",
+        "restore",
+        "result",
+        "resultType",
+        "resume",
+        "returnValue",
+        "rev",
+        "reverse",
+        "reversed",
+        "revocable",
+        "revokeObjectURL",
+        "rgbColor",
+        "right",
+        "rightContext",
+        "rightMargin",
+        "rolloffFactor",
+        "root",
+        "rootElement",
+        "rotate",
+        "rotateAxisAngle",
+        "rotateAxisAngleSelf",
+        "rotateFromVector",
+        "rotateFromVectorSelf",
+        "rotateSelf",
+        "rotation",
+        "rotationRate",
+        "round",
+        "rowIndex",
+        "rowSpan",
+        "rows",
+        "rubyAlign",
+        "rubyOverhang",
+        "rubyPosition",
+        "rules",
+        "runtime",
+        "runtimeStyle",
+        "rx",
+        "ry",
+        "safari",
+        "sampleCoverage",
+        "sampleRate",
+        "sandbox",
+        "save",
+        "scale",
+        "scale3d",
+        "scale3dSelf",
+        "scaleNonUniform",
+        "scaleNonUniformSelf",
+        "scaleSelf",
+        "scheme",
+        "scissor",
+        "scope",
+        "scopeName",
+        "scoped",
+        "screen",
+        "screenBrightness",
+        "screenEnabled",
+        "screenLeft",
+        "screenPixelToMillimeterX",
+        "screenPixelToMillimeterY",
+        "screenTop",
+        "screenX",
+        "screenY",
+        "scripts",
+        "scroll",
+        "scroll-behavior",
+        "scrollAmount",
+        "scrollBehavior",
+        "scrollBy",
+        "scrollByLines",
+        "scrollByPages",
+        "scrollDelay",
+        "scrollHeight",
+        "scrollIntoView",
+        "scrollIntoViewIfNeeded",
+        "scrollLeft",
+        "scrollLeftMax",
+        "scrollMaxX",
+        "scrollMaxY",
+        "scrollTo",
+        "scrollTop",
+        "scrollTopMax",
+        "scrollWidth",
+        "scrollX",
+        "scrollY",
+        "scrollbar3dLightColor",
+        "scrollbarArrowColor",
+        "scrollbarBaseColor",
+        "scrollbarDarkShadowColor",
+        "scrollbarFaceColor",
+        "scrollbarHighlightColor",
+        "scrollbarShadowColor",
+        "scrollbarTrackColor",
+        "scrollbars",
+        "scrolling",
+        "sdp",
+        "sdpMLineIndex",
+        "sdpMid",
+        "seal",
+        "search",
+        "searchBox",
+        "searchBoxJavaBridge_",
+        "searchParams",
+        "sectionRowIndex",
+        "secureConnectionStart",
+        "security",
+        "seed",
+        "seekable",
+        "seeking",
+        "select",
+        "selectAllChildren",
+        "selectNode",
+        "selectNodeContents",
+        "selectNodes",
+        "selectSingleNode",
+        "selectSubString",
+        "selected",
+        "selectedIndex",
+        "selectedOptions",
+        "selectedStyleSheetSet",
+        "selectedStylesheetSet",
+        "selection",
+        "selectionDirection",
+        "selectionEnd",
+        "selectionStart",
+        "selector",
+        "selectorText",
+        "self",
+        "send",
+        "sendAsBinary",
+        "sendBeacon",
+        "sender",
+        "sentTimestamp",
+        "separator",
+        "serializeToString",
+        "serviceWorker",
+        "sessionId",
+        "sessionStorage",
+        "set",
+        "setActive",
+        "setAlpha",
+        "setAttribute",
+        "setAttributeNS",
+        "setAttributeNode",
+        "setAttributeNodeNS",
+        "setBaseAndExtent",
+        "setBingCurrentSearchDefault",
+        "setCapture",
+        "setColor",
+        "setCompositeOperation",
+        "setCurrentTime",
+        "setCustomValidity",
+        "setData",
+        "setDate",
+        "setDragImage",
+        "setEnd",
+        "setEndAfter",
+        "setEndBefore",
+        "setEndPoint",
+        "setFillColor",
+        "setFilterRes",
+        "setFloat32",
+        "setFloat64",
+        "setFloatValue",
+        "setFullYear",
+        "setHours",
+        "setImmediate",
+        "setInt16",
+        "setInt32",
+        "setInt8",
+        "setInterval",
+        "setItem",
+        "setLineCap",
+        "setLineDash",
+        "setLineJoin",
+        "setLineWidth",
+        "setLocalDescription",
+        "setMatrix",
+        "setMatrixValue",
+        "setMediaKeys",
+        "setMilliseconds",
+        "setMinutes",
+        "setMiterLimit",
+        "setMonth",
+        "setNamedItem",
+        "setNamedItemNS",
+        "setNonUserCodeExceptions",
+        "setOrientToAngle",
+        "setOrientToAuto",
+        "setOrientation",
+        "setOverrideHistoryNavigationMode",
+        "setPaint",
+        "setParameter",
+        "setPeriodicWave",
+        "setPointerCapture",
+        "setPosition",
+        "setPreference",
+        "setProperty",
+        "setPrototypeOf",
+        "setRGBColor",
+        "setRGBColorICCColor",
+        "setRadius",
+        "setRangeText",
+        "setRemoteDescription",
+        "setRequestHeader",
+        "setResizable",
+        "setResourceTimingBufferSize",
+        "setRotate",
+        "setScale",
+        "setSeconds",
+        "setSelectionRange",
+        "setServerCertificate",
+        "setShadow",
+        "setSkewX",
+        "setSkewY",
+        "setStart",
+        "setStartAfter",
+        "setStartBefore",
+        "setStdDeviation",
+        "setStringValue",
+        "setStrokeColor",
+        "setSuggestResult",
+        "setTargetAtTime",
+        "setTargetValueAtTime",
+        "setTime",
+        "setTimeout",
+        "setTransform",
+        "setTranslate",
+        "setUTCDate",
+        "setUTCFullYear",
+        "setUTCHours",
+        "setUTCMilliseconds",
+        "setUTCMinutes",
+        "setUTCMonth",
+        "setUTCSeconds",
+        "setUint16",
+        "setUint32",
+        "setUint8",
+        "setUri",
+        "setValueAtTime",
+        "setValueCurveAtTime",
+        "setVariable",
+        "setVelocity",
+        "setVersion",
+        "setYear",
+        "settingName",
+        "settingValue",
+        "sex",
+        "shaderSource",
+        "shadowBlur",
+        "shadowColor",
+        "shadowOffsetX",
+        "shadowOffsetY",
+        "shadowRoot",
+        "shape",
+        "shape-rendering",
+        "shapeRendering",
+        "sheet",
+        "shift",
+        "shiftKey",
+        "shiftLeft",
+        "show",
+        "showHelp",
+        "showModal",
+        "showModalDialog",
+        "showModelessDialog",
+        "showNotification",
+        "sidebar",
+        "sign",
+        "signalingState",
+        "sin",
+        "singleNodeValue",
+        "sinh",
+        "size",
+        "sizeToContent",
+        "sizes",
+        "skewX",
+        "skewXSelf",
+        "skewY",
+        "skewYSelf",
+        "slice",
+        "slope",
+        "small",
+        "smil",
+        "smoothingTimeConstant",
+        "snapToLines",
+        "snapshotItem",
+        "snapshotLength",
+        "some",
+        "sort",
+        "source",
+        "sourceBuffer",
+        "sourceBuffers",
+        "sourceIndex",
+        "spacing",
+        "span",
+        "speakAs",
+        "speaking",
+        "specified",
+        "specularConstant",
+        "specularExponent",
+        "speechSynthesis",
+        "speed",
+        "speedOfSound",
+        "spellcheck",
+        "splice",
+        "split",
+        "splitText",
+        "spreadMethod",
+        "sqrt",
+        "src",
+        "srcElement",
+        "srcFilter",
+        "srcUrn",
+        "srcdoc",
+        "srclang",
+        "srcset",
+        "stack",
+        "stackTraceLimit",
+        "stacktrace",
+        "standalone",
+        "standby",
+        "start",
+        "startContainer",
+        "startIce",
+        "startOffset",
+        "startRendering",
+        "startTime",
+        "startsWith",
+        "state",
+        "status",
+        "statusMessage",
+        "statusText",
+        "statusbar",
+        "stdDeviationX",
+        "stdDeviationY",
+        "stencilFunc",
+        "stencilFuncSeparate",
+        "stencilMask",
+        "stencilMaskSeparate",
+        "stencilOp",
+        "stencilOpSeparate",
+        "step",
+        "stepDown",
+        "stepMismatch",
+        "stepUp",
+        "sticky",
+        "stitchTiles",
+        "stop",
+        "stop-color",
+        "stop-opacity",
+        "stopColor",
+        "stopImmediatePropagation",
+        "stopOpacity",
+        "stopPropagation",
+        "storageArea",
+        "storageName",
+        "storageStatus",
+        "storeSiteSpecificTrackingException",
+        "storeWebWideTrackingException",
+        "stpVersion",
+        "stream",
+        "strike",
+        "stringValue",
+        "stringify",
+        "stroke",
+        "stroke-dasharray",
+        "stroke-dashoffset",
+        "stroke-linecap",
+        "stroke-linejoin",
+        "stroke-miterlimit",
+        "stroke-opacity",
+        "stroke-width",
+        "strokeDasharray",
+        "strokeDashoffset",
+        "strokeLinecap",
+        "strokeLinejoin",
+        "strokeMiterlimit",
+        "strokeOpacity",
+        "strokeRect",
+        "strokeStyle",
+        "strokeText",
+        "strokeWidth",
+        "style",
+        "styleFloat",
+        "styleMedia",
+        "styleSheet",
+        "styleSheetSets",
+        "styleSheets",
+        "sub",
+        "subarray",
+        "subject",
+        "submit",
+        "subscribe",
+        "substr",
+        "substring",
+        "substringData",
+        "subtle",
+        "suffix",
+        "suffixes",
+        "summary",
+        "sup",
+        "supports",
+        "surfaceScale",
+        "surroundContents",
+        "suspend",
+        "suspendRedraw",
+        "swapCache",
+        "swapNode",
+        "sweepFlag",
+        "symbols",
+        "system",
+        "systemCode",
+        "systemId",
+        "systemLanguage",
+        "systemXDPI",
+        "systemYDPI",
+        "tBodies",
+        "tFoot",
+        "tHead",
+        "tabIndex",
+        "table",
+        "table-layout",
+        "tableLayout",
+        "tableValues",
+        "tag",
+        "tagName",
+        "tagUrn",
+        "tags",
+        "taintEnabled",
+        "takeRecords",
+        "tan",
+        "tanh",
+        "target",
+        "targetElement",
+        "targetTouches",
+        "targetX",
+        "targetY",
+        "tel",
+        "terminate",
+        "test",
+        "texImage2D",
+        "texParameterf",
+        "texParameteri",
+        "texSubImage2D",
+        "text",
+        "text-align",
+        "text-anchor",
+        "text-decoration",
+        "text-decoration-color",
+        "text-decoration-line",
+        "text-decoration-style",
+        "text-indent",
+        "text-overflow",
+        "text-rendering",
+        "text-shadow",
+        "text-transform",
+        "textAlign",
+        "textAlignLast",
+        "textAnchor",
+        "textAutospace",
+        "textBaseline",
+        "textContent",
+        "textDecoration",
+        "textDecorationBlink",
+        "textDecorationColor",
+        "textDecorationLine",
+        "textDecorationLineThrough",
+        "textDecorationNone",
+        "textDecorationOverline",
+        "textDecorationStyle",
+        "textDecorationUnderline",
+        "textIndent",
+        "textJustify",
+        "textJustifyTrim",
+        "textKashida",
+        "textKashidaSpace",
+        "textLength",
+        "textOverflow",
+        "textRendering",
+        "textShadow",
+        "textTracks",
+        "textTransform",
+        "textUnderlinePosition",
+        "then",
+        "threadId",
+        "threshold",
+        "tiltX",
+        "tiltY",
+        "time",
+        "timeEnd",
+        "timeStamp",
+        "timeout",
+        "timestamp",
+        "timestampOffset",
+        "timing",
+        "title",
+        "toArray",
+        "toBlob",
+        "toDataURL",
+        "toDateString",
+        "toElement",
+        "toExponential",
+        "toFixed",
+        "toFloat32Array",
+        "toFloat64Array",
+        "toGMTString",
+        "toISOString",
+        "toJSON",
+        "toLocaleDateString",
+        "toLocaleFormat",
+        "toLocaleLowerCase",
+        "toLocaleString",
+        "toLocaleTimeString",
+        "toLocaleUpperCase",
+        "toLowerCase",
+        "toMethod",
+        "toPrecision",
+        "toSdp",
+        "toSource",
+        "toStaticHTML",
+        "toString",
+        "toStringTag",
+        "toTimeString",
+        "toUTCString",
+        "toUpperCase",
+        "toggle",
+        "toggleLongPressEnabled",
+        "tooLong",
+        "toolbar",
+        "top",
+        "topMargin",
+        "total",
+        "totalFrameDelay",
+        "totalVideoFrames",
+        "touchAction",
+        "touches",
+        "trace",
+        "track",
+        "transaction",
+        "transactions",
+        "transform",
+        "transform-origin",
+        "transform-style",
+        "transformOrigin",
+        "transformPoint",
+        "transformString",
+        "transformStyle",
+        "transformToDocument",
+        "transformToFragment",
+        "transition",
+        "transition-delay",
+        "transition-duration",
+        "transition-property",
+        "transition-timing-function",
+        "transitionDelay",
+        "transitionDuration",
+        "transitionProperty",
+        "transitionTimingFunction",
+        "translate",
+        "translateSelf",
+        "translationX",
+        "translationY",
+        "trim",
+        "trimLeft",
+        "trimRight",
+        "trueSpeed",
+        "trunc",
+        "truncate",
+        "type",
+        "typeDetail",
+        "typeMismatch",
+        "typeMustMatch",
+        "types",
+        "ubound",
+        "undefined",
+        "unescape",
+        "uneval",
+        "unicode-bidi",
+        "unicodeBidi",
+        "uniform1f",
+        "uniform1fv",
+        "uniform1i",
+        "uniform1iv",
+        "uniform2f",
+        "uniform2fv",
+        "uniform2i",
+        "uniform2iv",
+        "uniform3f",
+        "uniform3fv",
+        "uniform3i",
+        "uniform3iv",
+        "uniform4f",
+        "uniform4fv",
+        "uniform4i",
+        "uniform4iv",
+        "uniformMatrix2fv",
+        "uniformMatrix3fv",
+        "uniformMatrix4fv",
+        "unique",
+        "uniqueID",
+        "uniqueNumber",
+        "unitType",
+        "units",
+        "unloadEventEnd",
+        "unloadEventStart",
+        "unlock",
+        "unmount",
+        "unobserve",
+        "unpause",
+        "unpauseAnimations",
+        "unreadCount",
+        "unregister",
+        "unregisterContentHandler",
+        "unregisterProtocolHandler",
+        "unscopables",
+        "unselectable",
+        "unshift",
+        "unsubscribe",
+        "unsuspendRedraw",
+        "unsuspendRedrawAll",
+        "unwatch",
+        "unwrapKey",
+        "update",
+        "updateCommands",
+        "updateIce",
+        "updateInterval",
+        "updateSettings",
+        "updated",
+        "updating",
+        "upload",
+        "upper",
+        "upperBound",
+        "upperOpen",
+        "uri",
+        "url",
+        "urn",
+        "urns",
+        "usages",
+        "useCurrentView",
+        "useMap",
+        "useProgram",
+        "usedSpace",
+        "userAgent",
+        "userLanguage",
+        "username",
+        "v8BreakIterator",
+        "vAlign",
+        "vLink",
+        "valid",
+        "validateProgram",
+        "validationMessage",
+        "validity",
+        "value",
+        "valueAsDate",
+        "valueAsNumber",
+        "valueAsString",
+        "valueInSpecifiedUnits",
+        "valueMissing",
+        "valueOf",
+        "valueText",
+        "valueType",
+        "values",
+        "vector-effect",
+        "vectorEffect",
+        "velocityAngular",
+        "velocityExpansion",
+        "velocityX",
+        "velocityY",
+        "vendor",
+        "vendorSub",
+        "verify",
+        "version",
+        "vertexAttrib1f",
+        "vertexAttrib1fv",
+        "vertexAttrib2f",
+        "vertexAttrib2fv",
+        "vertexAttrib3f",
+        "vertexAttrib3fv",
+        "vertexAttrib4f",
+        "vertexAttrib4fv",
+        "vertexAttribDivisorANGLE",
+        "vertexAttribPointer",
+        "vertical",
+        "vertical-align",
+        "verticalAlign",
+        "verticalOverflow",
+        "vibrate",
+        "videoHeight",
+        "videoTracks",
+        "videoWidth",
+        "view",
+        "viewBox",
+        "viewBoxString",
+        "viewTarget",
+        "viewTargetString",
+        "viewport",
+        "viewportAnchorX",
+        "viewportAnchorY",
+        "viewportElement",
+        "visibility",
+        "visibilityState",
+        "visible",
+        "vlinkColor",
+        "voice",
+        "volume",
+        "vrml",
+        "vspace",
+        "w",
+        "wand",
+        "warn",
+        "wasClean",
+        "watch",
+        "watchPosition",
+        "webdriver",
+        "webkitAddKey",
+        "webkitAnimation",
+        "webkitAnimationDelay",
+        "webkitAnimationDirection",
+        "webkitAnimationDuration",
+        "webkitAnimationFillMode",
+        "webkitAnimationIterationCount",
+        "webkitAnimationName",
+        "webkitAnimationPlayState",
+        "webkitAnimationTimingFunction",
+        "webkitAppearance",
+        "webkitAudioContext",
+        "webkitAudioDecodedByteCount",
+        "webkitAudioPannerNode",
+        "webkitBackfaceVisibility",
+        "webkitBackground",
+        "webkitBackgroundAttachment",
+        "webkitBackgroundClip",
+        "webkitBackgroundColor",
+        "webkitBackgroundImage",
+        "webkitBackgroundOrigin",
+        "webkitBackgroundPosition",
+        "webkitBackgroundPositionX",
+        "webkitBackgroundPositionY",
+        "webkitBackgroundRepeat",
+        "webkitBackgroundSize",
+        "webkitBackingStorePixelRatio",
+        "webkitBorderImage",
+        "webkitBorderImageOutset",
+        "webkitBorderImageRepeat",
+        "webkitBorderImageSlice",
+        "webkitBorderImageSource",
+        "webkitBorderImageWidth",
+        "webkitBoxAlign",
+        "webkitBoxDirection",
+        "webkitBoxFlex",
+        "webkitBoxOrdinalGroup",
+        "webkitBoxOrient",
+        "webkitBoxPack",
+        "webkitBoxSizing",
+        "webkitCancelAnimationFrame",
+        "webkitCancelFullScreen",
+        "webkitCancelKeyRequest",
+        "webkitCancelRequestAnimationFrame",
+        "webkitClearResourceTimings",
+        "webkitClosedCaptionsVisible",
+        "webkitConvertPointFromNodeToPage",
+        "webkitConvertPointFromPageToNode",
+        "webkitCreateShadowRoot",
+        "webkitCurrentFullScreenElement",
+        "webkitCurrentPlaybackTargetIsWireless",
+        "webkitDirectionInvertedFromDevice",
+        "webkitDisplayingFullscreen",
+        "webkitEnterFullScreen",
+        "webkitEnterFullscreen",
+        "webkitExitFullScreen",
+        "webkitExitFullscreen",
+        "webkitExitPointerLock",
+        "webkitFullScreenKeyboardInputAllowed",
+        "webkitFullscreenElement",
+        "webkitFullscreenEnabled",
+        "webkitGenerateKeyRequest",
+        "webkitGetAsEntry",
+        "webkitGetDatabaseNames",
+        "webkitGetEntries",
+        "webkitGetEntriesByName",
+        "webkitGetEntriesByType",
+        "webkitGetFlowByName",
+        "webkitGetGamepads",
+        "webkitGetImageDataHD",
+        "webkitGetNamedFlows",
+        "webkitGetRegionFlowRanges",
+        "webkitGetUserMedia",
+        "webkitHasClosedCaptions",
+        "webkitHidden",
+        "webkitIDBCursor",
+        "webkitIDBDatabase",
+        "webkitIDBDatabaseError",
+        "webkitIDBDatabaseException",
+        "webkitIDBFactory",
+        "webkitIDBIndex",
+        "webkitIDBKeyRange",
+        "webkitIDBObjectStore",
+        "webkitIDBRequest",
+        "webkitIDBTransaction",
+        "webkitImageSmoothingEnabled",
+        "webkitIndexedDB",
+        "webkitInitMessageEvent",
+        "webkitIsFullScreen",
+        "webkitKeys",
+        "webkitLineDashOffset",
+        "webkitLockOrientation",
+        "webkitMatchesSelector",
+        "webkitMediaStream",
+        "webkitNotifications",
+        "webkitOfflineAudioContext",
+        "webkitOrientation",
+        "webkitPeerConnection00",
+        "webkitPersistentStorage",
+        "webkitPointerLockElement",
+        "webkitPostMessage",
+        "webkitPreservesPitch",
+        "webkitPutImageDataHD",
+        "webkitRTCPeerConnection",
+        "webkitRegionOverset",
+        "webkitRequestAnimationFrame",
+        "webkitRequestFileSystem",
+        "webkitRequestFullScreen",
+        "webkitRequestFullscreen",
+        "webkitRequestPointerLock",
+        "webkitResolveLocalFileSystemURL",
+        "webkitSetMediaKeys",
+        "webkitSetResourceTimingBufferSize",
+        "webkitShadowRoot",
+        "webkitShowPlaybackTargetPicker",
+        "webkitSlice",
+        "webkitSpeechGrammar",
+        "webkitSpeechGrammarList",
+        "webkitSpeechRecognition",
+        "webkitSpeechRecognitionError",
+        "webkitSpeechRecognitionEvent",
+        "webkitStorageInfo",
+        "webkitSupportsFullscreen",
+        "webkitTemporaryStorage",
+        "webkitTextSizeAdjust",
+        "webkitTransform",
+        "webkitTransformOrigin",
+        "webkitTransition",
+        "webkitTransitionDelay",
+        "webkitTransitionDuration",
+        "webkitTransitionProperty",
+        "webkitTransitionTimingFunction",
+        "webkitURL",
+        "webkitUnlockOrientation",
+        "webkitUserSelect",
+        "webkitVideoDecodedByteCount",
+        "webkitVisibilityState",
+        "webkitWirelessVideoPlaybackDisabled",
+        "webkitdropzone",
+        "webstore",
+        "weight",
+        "whatToShow",
+        "wheelDelta",
+        "wheelDeltaX",
+        "wheelDeltaY",
+        "which",
+        "white-space",
+        "whiteSpace",
+        "wholeText",
+        "widows",
+        "width",
+        "will-change",
+        "willChange",
+        "willValidate",
+        "window",
+        "withCredentials",
+        "word-break",
+        "word-spacing",
+        "word-wrap",
+        "wordBreak",
+        "wordSpacing",
+        "wordWrap",
+        "wrap",
+        "wrapKey",
+        "write",
+        "writeln",
+        "writingMode",
+        "x",
+        "x1",
+        "x2",
+        "xChannelSelector",
+        "xmlEncoding",
+        "xmlStandalone",
+        "xmlVersion",
+        "xmlbase",
+        "xmllang",
+        "xmlspace",
+        "y",
+        "y1",
+        "y2",
+        "yChannelSelector",
+        "yandex",
+        "z",
+        "z-index",
+        "zIndex",
+        "zoom",
+        "zoomAndPan",
+        "zoomRectScreen"
+    ]
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/node.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/node.js
new file mode 100644
index 0000000..cbe49e3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/node.js
@@ -0,0 +1,253 @@
+var path = require("path");
+var fs = require("fs");
+var vm = require("vm");
+
+var UglifyJS = vm.createContext({
+    console       : console,
+    process       : process,
+    Buffer        : Buffer,
+    MOZ_SourceMap : require("source-map")
+});
+
+function load_global(file) {
+    file = path.resolve(path.dirname(module.filename), file);
+    try {
+        var code = fs.readFileSync(file, "utf8");
+        return vm.runInContext(code, UglifyJS, file);
+    } catch(ex) {
+        // XXX: in case of a syntax error, the message is kinda
+        // useless. (no location information).
+        console.log("ERROR in file: " + file + " / " + ex);
+        process.exit(1);
+    }
+};
+
+var FILES = exports.FILES = [
+    "../lib/utils.js",
+    "../lib/ast.js",
+    "../lib/parse.js",
+    "../lib/transform.js",
+    "../lib/scope.js",
+    "../lib/output.js",
+    "../lib/compress.js",
+    "../lib/sourcemap.js",
+    "../lib/mozilla-ast.js",
+    "../lib/propmangle.js"
+].map(function(file){
+    return fs.realpathSync(path.join(path.dirname(__filename), file));
+});
+
+FILES.forEach(load_global);
+
+UglifyJS.AST_Node.warn_function = function(txt) {
+    console.error("WARN: %s", txt);
+};
+
+// XXX: perhaps we shouldn't export everything but heck, I'm lazy.
+for (var i in UglifyJS) {
+    if (UglifyJS.hasOwnProperty(i)) {
+        exports[i] = UglifyJS[i];
+    }
+}
+
+exports.minify = function(files, options) {
+    options = UglifyJS.defaults(options, {
+        spidermonkey : false,
+        outSourceMap : null,
+        sourceRoot   : null,
+        inSourceMap  : null,
+        fromString   : false,
+        warnings     : false,
+        mangle       : {},
+        output       : null,
+        compress     : {}
+    });
+    UglifyJS.base54.reset();
+
+    // 1. parse
+    var toplevel = null,
+        sourcesContent = {};
+
+    if (options.spidermonkey) {
+        toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
+    } else {
+        if (typeof files == "string")
+            files = [ files ];
+        files.forEach(function(file){
+            var code = options.fromString
+                ? file
+                : fs.readFileSync(file, "utf8");
+            sourcesContent[file] = code;
+            toplevel = UglifyJS.parse(code, {
+                filename: options.fromString ? "?" : file,
+                toplevel: toplevel
+            });
+        });
+    }
+
+    // 2. compress
+    if (options.compress) {
+        var compress = { warnings: options.warnings };
+        UglifyJS.merge(compress, options.compress);
+        toplevel.figure_out_scope();
+        var sq = UglifyJS.Compressor(compress);
+        toplevel = toplevel.transform(sq);
+    }
+
+    // 3. mangle
+    if (options.mangle) {
+        toplevel.figure_out_scope(options.mangle);
+        toplevel.compute_char_frequency(options.mangle);
+        toplevel.mangle_names(options.mangle);
+    }
+
+    // 4. output
+    var inMap = options.inSourceMap;
+    var output = {};
+    if (typeof options.inSourceMap == "string") {
+        inMap = fs.readFileSync(options.inSourceMap, "utf8");
+    }
+    if (options.outSourceMap) {
+        output.source_map = UglifyJS.SourceMap({
+            file: options.outSourceMap,
+            orig: inMap,
+            root: options.sourceRoot
+        });
+        if (options.sourceMapIncludeSources) {
+            for (var file in sourcesContent) {
+                if (sourcesContent.hasOwnProperty(file)) {
+                    output.source_map.get().setSourceContent(file, sourcesContent[file]);
+                }
+            }
+        }
+
+    }
+    if (options.output) {
+        UglifyJS.merge(output, options.output);
+    }
+    var stream = UglifyJS.OutputStream(output);
+    toplevel.print(stream);
+
+    if(options.outSourceMap){
+        stream += "\n//# sourceMappingURL=" + options.outSourceMap;
+    }
+
+    var source_map = output.source_map;
+    if (source_map) {
+        source_map = source_map + "";
+    }
+
+    return {
+        code : stream + "",
+        map  : source_map
+    };
+};
+
+// exports.describe_ast = function() {
+//     function doitem(ctor) {
+//         var sub = {};
+//         ctor.SUBCLASSES.forEach(function(ctor){
+//             sub[ctor.TYPE] = doitem(ctor);
+//         });
+//         var ret = {};
+//         if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
+//         if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
+//         return ret;
+//     }
+//     return doitem(UglifyJS.AST_Node).sub;
+// }
+
+exports.describe_ast = function() {
+    var out = UglifyJS.OutputStream({ beautify: true });
+    function doitem(ctor) {
+        out.print("AST_" + ctor.TYPE);
+        var props = ctor.SELF_PROPS.filter(function(prop){
+            return !/^\$/.test(prop);
+        });
+        if (props.length > 0) {
+            out.space();
+            out.with_parens(function(){
+                props.forEach(function(prop, i){
+                    if (i) out.space();
+                    out.print(prop);
+                });
+            });
+        }
+        if (ctor.documentation) {
+            out.space();
+            out.print_string(ctor.documentation);
+        }
+        if (ctor.SUBCLASSES.length > 0) {
+            out.space();
+            out.with_block(function(){
+                ctor.SUBCLASSES.forEach(function(ctor, i){
+                    out.indent();
+                    doitem(ctor);
+                    out.newline();
+                });
+            });
+        }
+    };
+    doitem(UglifyJS.AST_Node);
+    return out + "";
+};
+
+function readReservedFile(filename, reserved) {
+    if (!reserved) {
+        reserved = { vars: [], props: [] };
+    }
+    var data = fs.readFileSync(filename, "utf8");
+    data = JSON.parse(data);
+    if (data.vars) {
+        data.vars.forEach(function(name){
+            UglifyJS.push_uniq(reserved.vars, name);
+        });
+    }
+    if (data.props) {
+        data.props.forEach(function(name){
+            UglifyJS.push_uniq(reserved.props, name);
+        });
+    }
+    return reserved;
+}
+
+exports.readReservedFile = readReservedFile;
+
+exports.readDefaultReservedFile = function(reserved) {
+    return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
+};
+
+exports.readNameCache = function(filename, key) {
+    var cache = null;
+    if (filename) {
+        try {
+            var cache = fs.readFileSync(filename, "utf8");
+            cache = JSON.parse(cache)[key];
+            if (!cache) throw "init";
+            cache.props = UglifyJS.Dictionary.fromObject(cache.props);
+        } catch(ex) {
+            cache = {
+                cname: -1,
+                props: new UglifyJS.Dictionary()
+            };
+        }
+    }
+    return cache;
+};
+
+exports.writeNameCache = function(filename, key, cache) {
+    if (filename) {
+        var data;
+        try {
+            data = fs.readFileSync(filename, "utf8");
+            data = JSON.parse(data);
+        } catch(ex) {
+            data = {};
+        }
+        data[key] = {
+            cname: cache.cname,
+            props: cache.props.toObject()
+        };
+        fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
+    }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/props.html b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/props.html
new file mode 100644
index 0000000..f7c777a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/node_modules/uglify-js/tools/props.html
@@ -0,0 +1,61 @@
+<html>
+  <head>
+  </head>
+  <body>
+    <script>(function(){
+      var props = {};
+
+      function addObject(obj) {
+        if (obj == null) return;
+        try {
+          Object.getOwnPropertyNames(obj).forEach(add);
+        } catch(ex) {}
+        if (obj.prototype) {
+          Object.getOwnPropertyNames(obj.prototype).forEach(add);
+        }
+        if (typeof obj == "function") {
+          try {
+            Object.getOwnPropertyNames(new obj).forEach(add);
+          } catch(ex) {}
+        }
+      }
+
+      function add(name) {
+        props[name] = true;
+      }
+
+      Object.getOwnPropertyNames(window).forEach(function(thing){
+        addObject(window[thing]);
+      });
+
+      try {
+        addObject(new Event("click"));
+        addObject(new Event("contextmenu"));
+        addObject(new Event("mouseup"));
+        addObject(new Event("mousedown"));
+        addObject(new Event("keydown"));
+        addObject(new Event("keypress"));
+        addObject(new Event("keyup"));
+      } catch(ex) {}
+
+      var ta = document.createElement("textarea");
+      ta.style.width = "100%";
+      ta.style.height = "20em";
+      ta.style.boxSizing = "border-box";
+      <!-- ta.value = Object.keys(props).sort(cmp).map(function(name){ -->
+      <!--   return JSON.stringify(name); -->
+      <!-- }).join(",\n"); -->
+      ta.value = JSON.stringify({
+        vars: [],
+        props: Object.keys(props).sort(cmp)
+      }, null, 2);
+      document.body.appendChild(ta);
+
+      function cmp(a, b) {
+        a = a.toLowerCase();
+        b = b.toLowerCase();
+        return a < b ? -1 : a > b ? 1 : 0;
+      }
+    })();</script>
+  </body>
+</html>
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/package.json
new file mode 100644
index 0000000..4171d04
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-filters/package.json
@@ -0,0 +1,55 @@
+{
+  "name": "jade-filters",
+  "version": "0.0.3",
+  "description": "Code for processing filters in jade templates",
+  "keywords": [],
+  "dependencies": {
+    "clean-css": "^3.3.0",
+    "constantinople": "^3.0.1",
+    "jade-error": "^1.0.0",
+    "jade-walk": "0.0.2",
+    "jstransformer": "0.0.2",
+    "resolve": "^1.1.6",
+    "uglify-js": "^2.4.23"
+  },
+  "devDependencies": {
+    "mocha": "*"
+  },
+  "scripts": {
+    "test": "mocha -R spec"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-filters.git"
+  },
+  "author": {
+    "name": "Forbes Lindesay"
+  },
+  "license": "MIT",
+  "gitHead": "f33c2f73d33edc6ef35c99d6ba71439340a5d536",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-filters/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-filters",
+  "_id": "jade-filters@0.0.3",
+  "_shasum": "1da9a85a76ba4791180ea31e2efa01a55e3e7d9c",
+  "_from": "jade-filters@*",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "1da9a85a76ba4791180ea31e2efa01a55e3e7d9c",
+    "tarball": "http://registry.npmjs.org/jade-filters/-/jade-filters-0.0.3.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-filters/-/jade-filters-0.0.3.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/.npmignore
new file mode 100644
index 0000000..95566bc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/.npmignore
@@ -0,0 +1,15 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
+*.actual.json
+coverage
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/LICENSE
new file mode 100644
index 0000000..27cc9f3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/README.md
new file mode 100644
index 0000000..843be7f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/README.md
@@ -0,0 +1,49 @@
+# jade-lexer
+
+The jade lexer.  This module is responsible for taking a string and converting it into an array of tokens.
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-lexer/master.svg)](https://travis-ci.org/jadejs/jade-lexer)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-lexer.svg)](https://gemnasium.com/jadejs/jade-lexer)
+[![NPM version](https://img.shields.io/npm/v/jade-lexer.svg)](https://www.npmjs.org/package/jade-lexer)
+
+## Installation
+
+    npm install jade-lexer
+
+## Usage
+
+```js
+var lex = require('jade-lexer');
+
+console.log(JSON.stringify(lex('div(data-foo="bar")', 'my-file.jade'), null, '  '))
+```
+
+```json
+[
+  {
+    "type": "tag",
+    "line": 1,
+    "val": "div",
+    "selfClosing": false
+  },
+  {
+    "type": "attrs",
+    "line": 1,
+    "attrs": [
+      {
+        "name": "data-foo",
+        "val": "\"bar\"",
+        "escaped": true
+      }
+    ]
+  },
+  {
+    "type": "eos",
+    "line": 1
+  }
+]
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/index.js
new file mode 100644
index 0000000..02fc528
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/index.js
@@ -0,0 +1,1047 @@
+'use strict';
+
+var assert = require('assert');
+var characterParser = require('character-parser');
+
+module.exports = lex;
+module.exports.Lexer = Lexer;
+function lex(str, filename) {
+  var lexer = new Lexer(str, filename);
+  return JSON.parse(JSON.stringify(lexer.getTokens()));
+}
+
+/**
+ * Initialize `Lexer` with the given `str`.
+ *
+ * @param {String} str
+ * @param {String} filename
+ * @api private
+ */
+
+function Lexer(str, filename, options) {
+  options = options || {};
+  //Strip any UTF-8 BOM off of the start of `str`, if it exists.
+  str = str.replace(/^\uFEFF/, '');
+  this.input = str.replace(/\r\n|\r/g, '\n');
+  this.filename = filename;
+  this.interpolated = options.interpolated || false;
+  this.lastIndents = 0;
+  this.lineno = options.startingLine || 1;
+  this.indentStack = [];
+  this.indentRe = null;
+  this.pipeless = false;
+
+  this.tokens = [];
+  this.ended = false;
+};
+
+/**
+ * Lexer prototype.
+ */
+
+Lexer.prototype = {
+
+  constructor: Lexer,
+
+  error: function (message, code) {
+    var err = new Error(message + ' on line ' + this.lineno + ' of ' + (this.filename || 'jade'));
+    err.code = 'JADE:' + code;
+    err.msg = message;
+    err.line = this.lineno;
+    err.filename = this.filename;
+    throw err;
+  },
+
+  assert: function (value, message) {
+    if (!value) this.error(message, 'ASSERT_FAILED');
+  },
+
+  assertExpression: function (exp) {
+    //this verifies that a JavaScript expression is valid
+    try {
+      Function('', 'return (' + exp + ')');
+    } catch (ex) {
+      this.error('Syntax Error', 'SYNTAX_ERROR');
+    }
+  },
+
+  assertNestingCorrect: function (exp) {
+    //this verifies that code is properly nested, but allows
+    //invalid JavaScript such as the contents of `attributes`
+    var res = characterParser(exp)
+    if (res.isNesting()) {
+      this.error('Nesting must match on expression `' + exp + '`', 'INCORRECT_NESTING')
+    }
+  },
+
+  /**
+   * Construct a token with the given `type` and `val`.
+   *
+   * @param {String} type
+   * @param {String} val
+   * @return {Object}
+   * @api private
+   */
+
+  tok: function(type, val){
+    return val === undefined ?
+      {type: type, line: this.lineno} :
+      {type: type, line: this.lineno, val: val};
+  },
+
+  /**
+   * Consume the given `len` of input.
+   *
+   * @param {Number} len
+   * @api private
+   */
+
+  consume: function(len){
+    this.input = this.input.substr(len);
+  },
+
+  /**
+   * Scan for `type` with the given `regexp`.
+   *
+   * @param {String} type
+   * @param {RegExp} regexp
+   * @return {Object}
+   * @api private
+   */
+
+  scan: function(regexp, type){
+    var captures;
+    if (captures = regexp.exec(this.input)) {
+      this.consume(captures[0].length);
+      return this.tok(type, captures[1]);
+    }
+  },
+
+  /**
+   * Return the indexOf `(` or `{` or `[` / `)` or `}` or `]` delimiters.
+   *
+   * @return {Number}
+   * @api private
+   */
+
+  bracketExpression: function(skip){
+    skip = skip || 0;
+    var start = this.input[skip];
+    assert(start === '(' || start === '{' || start === '[',
+           'The start character should be "(", "{" or "["');
+    var end = ({'(': ')', '{': '}', '[': ']'})[start];
+    var range;
+    try {
+      range = characterParser.parseMax(this.input, {start: skip + 1});
+    } catch (ex) {
+      this.error(ex.message, 'BRACKET_MISMATCH');
+    }
+    this.assert(this.input[range.end] === end,
+           'start character "' + start + '" should match end character "' + this.input[range.end] + '"');
+    return range;
+  },
+
+  /**
+   * end-of-source.
+   */
+
+  eos: function() {
+    if (this.input.length) return;
+    for (var i = 0; i < this.indentStack.length; i++) {
+      this.tokens.push(this.tok('outdent'));
+    }
+    this.tokens.push(this.tok('eos'));
+    this.ended = true;
+    return true;
+  },
+
+  /**
+   * Blank line.
+   */
+
+  blank: function() {
+    var captures;
+    if (captures = /^\n *\n/.exec(this.input)) {
+      this.consume(captures[0].length - 1);
+      ++this.lineno;
+      if (this.pipeless) this.tokens.push(this.tok('text', ''));
+      return true;
+    }
+  },
+
+  /**
+   * Comment.
+   */
+
+  comment: function() {
+    var captures;
+    if (captures = /^\/\/(-)?([^\n]*)/.exec(this.input)) {
+      this.consume(captures[0].length);
+      var tok = this.tok('comment', captures[2]);
+      tok.buffer = '-' != captures[1];
+      this.pipeless = true;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Interpolated tag.
+   */
+
+  interpolation: function() {
+    if (/^#\{/.test(this.input)) {
+      var match = this.bracketExpression(1);
+      this.consume(match.end + 1);
+      this.tokens.push(this.tok('interpolation', match.src));
+      return true;
+    }
+  },
+
+  /**
+   * Tag.
+   */
+
+  tag: function() {
+    var captures;
+    if (captures = /^(\w(?:[-:\w]*\w)?)(\/?)/.exec(this.input)) {
+      this.consume(captures[0].length);
+      var tok, name = captures[1];
+      tok = this.tok('tag', name);
+      tok.selfClosing = !!captures[2];
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Filter.
+   */
+
+  filter: function() {
+    var tok = this.scan(/^:([\w\-]+)/, 'filter');
+    if (tok) {
+      this.pipeless = true;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Doctype.
+   */
+
+  doctype: function() {
+    if (this.scan(/^!!! *([^\n]+)?/, 'doctype')) {
+      this.error('`!!!` is deprecated, you must now use `doctype`', 'OLD_DOCTYPE');
+    }
+    var node = this.scan(/^(?:doctype) *([^\n]+)?/, 'doctype');
+    if (node && node.val && node.val.trim() === '5') {
+      this.error('`doctype 5` is deprecated, you must now use `doctype html`', 'OLD_DOCTYPE');
+    }
+    if (node) {
+      this.tokens.push(node);
+      return true;
+    }
+  },
+
+  /**
+   * Id.
+   */
+
+  id: function() {
+    var tok = this.scan(/^#([\w-]+)/, 'id');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Class.
+   */
+
+  className: function() {
+    var tok = this.scan(/^\.([\w-]+)/, 'class');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Text.
+   */
+  endInterpolation: function () {
+    if (this.interpolated && this.input[0] === ']') {
+      this.input = this.input.substr(1);
+      this.ended = true;
+      return true;
+    }
+  },
+  addText: function (value, prefix) {
+    if (value + prefix === '') return;
+    prefix = prefix || '';
+    var indexOfEnd = this.interpolated ? value.indexOf(']') : -1;
+    var indexOfStart = value.indexOf('#[');
+    var indexOfEscaped = value.indexOf('\\#[');
+
+    if (indexOfEnd === -1) indexOfEnd = Infinity;
+    if (indexOfStart === -1) indexOfStart = Infinity;
+    if (indexOfEscaped === -1) indexOfEscaped = Infinity;
+
+    if (indexOfEscaped !== Infinity && indexOfEscaped < indexOfEnd && indexOfEscaped < indexOfStart) {
+      prefix = prefix + value.substr(0, value.indexOf('\\#[')) + '#[';
+      return this.addText(value.substr(value.indexOf('\\#[') + 3), prefix);
+    }
+    if (indexOfStart !== Infinity && indexOfStart < indexOfEnd && indexOfStart < indexOfEscaped) {
+      this.tokens.push(this.tok('text', prefix + value.substr(0, indexOfStart)));
+      this.tokens.push(this.tok('start-jade-interpolation'));
+      var child = new this.constructor(value.substr(indexOfStart + 2), this.filename, {
+        interpolated: true,
+        startingLine: this.lineno
+      });
+      var interpolated = child.getTokens();
+      for (var i = 0; i < interpolated.length; i++) {
+        this.tokens.push(interpolated[i]);
+        if (interpolated[i].type === 'eos') {
+          this.error('End of line was reached with no closing bracket for interpolation.', 'NO_END_BRACKET');
+        }
+      }
+      this.tokens.push(this.tok('end-jade-interpolation'));
+      this.addText(child.input);
+      return;
+    }
+    if (indexOfEnd !== Infinity && indexOfEnd < indexOfStart && indexOfEnd < indexOfEscaped) {
+      if (prefix + value.substr(0, value.indexOf(']'))) {
+        this.tokens.push(this.tok('text', prefix + value.substr(0, value.indexOf(']'))));
+      }
+      this.ended = true;
+      this.input = value.substr(value.indexOf(']') + 1) + this.input;
+      return;
+    }
+
+    this.tokens.push(this.tok('text', prefix + value));
+  },
+
+  text: function() {
+    var tok = this.scan(/^(?:\| ?| )([^\n]+)/, 'text') ||
+      this.scan(/^\|?( )/, 'text');
+    if (tok) {
+      this.addText(tok.val);
+      return true;
+    }
+  },
+
+  textHtml: function () {
+    var tok = this.scan(/^(<[^\n]*)/, 'text-html');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  textFail: function () {
+    var tok;
+    if (tok = this.scan(/^([^\.\n][^\n]+)/, 'text')) {
+      console.warn('Warning: missing space before text for line ' + this.lineno +
+          ' of jade file "' + this.filename + '"');
+      this.addText(tok.val);
+      return true;
+    }
+  },
+
+  /**
+   * Dot.
+   */
+
+  dot: function() {
+    var tok;
+    if (tok = this.scan(/^\./, 'dot')) {
+      this.pipeless = true;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Extends.
+   */
+
+  "extends": function() {
+    var tok = this.scan(/^extends? +([^\n]+)/, 'extends');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Block prepend.
+   */
+
+  prepend: function() {
+    var captures;
+    if (captures = /^prepend +([^\n]+)/.exec(this.input)) {
+      var mode = 'prepend'
+        , name = captures[1].trim()
+        , tok = this.tok('block', name);
+      if (!name) return;
+      this.consume(captures[0].length);
+      tok.mode = mode;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Block append.
+   */
+
+  append: function() {
+    var captures;
+    if (captures = /^append +([^\n]+)/.exec(this.input)) {
+      var mode = 'append'
+        , name = captures[1].trim()
+        , tok = this.tok('block', name);
+      if (!name) return;
+      this.consume(captures[0].length);
+      tok.mode = mode;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Block.
+   */
+
+  block: function() {
+    var captures;
+    if (captures = /^block\b *(?:(prepend|append) +)?([^\n]+)/.exec(this.input)) {
+      var mode = captures[1] || 'replace'
+        , name = captures[2].trim()
+        , tok = this.tok('block', name);
+      if (!name) return;
+      this.consume(captures[0].length);
+
+      tok.mode = mode;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Mixin Block.
+   */
+
+  mixinBlock: function() {
+    var captures;
+    if (captures = /^block[ \t]*(\n|$)/.exec(this.input)) {
+      this.consume(captures[0].length - captures[1].length);
+      this.tokens.push(this.tok('mixin-block'));
+      return true;
+    }
+  },
+
+  /**
+   * Yield.
+   */
+
+  'yield': function() {
+    var tok = this.scan(/^yield */, 'yield');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Include.
+   */
+
+  include: function() {
+    var tok = this.scan(/^include +([^\n]+)/, 'include');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Include with filter
+   */
+
+  includeFiltered: function() {
+    var captures;
+    if (captures = /^include:([\w\-]+)([\( ])/.exec(this.input)) {
+      this.consume(captures[0].length - 1);
+      var filter = captures[1];
+      var attrs = captures[2] === '(' ? this.attrs() : null;
+      if (!(captures[2] === ' ' || this.input[0] === ' ')) {
+        this.error('expected space after include:filter but got ' + JSON.stringify(this.input[0]), 'NO_FILTER_SPACE');
+      }
+      captures = /^ *([^\n]+)/.exec(this.input);
+      if (!captures || captures[1].trim() === '') {
+        this.error('missing path for include:filter', 'NO_INCLUDE_PATH');
+      }
+      this.consume(captures[0].length);
+      var path = captures[1];
+      var tok = this.tok('include', path);
+      tok.filter = filter;
+      tok.attrs = attrs;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Case.
+   */
+
+  "case": function() {
+    var tok = this.scan(/^case +([^\n]+)/, 'case');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * When.
+   */
+
+  when: function() {
+    var tok = this.scan(/^when +([^:\n]+)/, 'when');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Default.
+   */
+
+  "default": function() {
+    var tok = this.scan(/^default */, 'default');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Call mixin.
+   */
+
+  call: function(){
+
+    var tok, captures;
+    if (captures = /^\+(\s*)(([-\w]+)|(#\{))/.exec(this.input)) {
+      // try to consume simple or interpolated call
+      if (captures[3]) {
+        // simple call
+        this.consume(captures[0].length);
+        tok = this.tok('call', captures[3]);
+      } else {
+        // interpolated call
+        var match = this.bracketExpression(2 + captures[1].length);
+        this.consume(match.end + 1);
+        this.assertExpression(match.src);
+        tok = this.tok('call', '#{'+match.src+'}');
+      }
+
+      // Check for args (not attributes)
+      if (captures = /^ *\(/.exec(this.input)) {
+        var range = this.bracketExpression(captures[0].length - 1);
+        if (!/^\s*[-\w]+ *=/.test(range.src)) { // not attributes
+          this.consume(range.end + 1);
+          tok.args = range.src;
+        }
+        if (tok.args) {
+          this.assertExpression('[' + tok.args + ']');
+        }
+      }
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Mixin.
+   */
+
+  mixin: function(){
+    var captures;
+    if (captures = /^mixin +([-\w]+)(?: *\((.*)\))? */.exec(this.input)) {
+      this.consume(captures[0].length);
+      var tok = this.tok('mixin', captures[1]);
+      tok.args = captures[2] || null;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Conditional.
+   */
+
+  conditional: function() {
+    var captures;
+    if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
+      this.consume(captures[0].length);
+      var type = captures[1]
+      var js = captures[2];
+      var isIf = false;
+      var isElse = false;
+
+      switch (type) {
+        case 'if':
+          this.assertExpression(js)
+          js = 'if (' + js + ')';
+          isIf = true;
+          break;
+        case 'unless':
+          this.assertExpression(js)
+          js = 'if (!(' + js + '))';
+          isIf = true;
+          break;
+        case 'else if':
+          this.assertExpression(js)
+          js = 'else if (' + js + ')';
+          isIf = true;
+          isElse = true;
+          break;
+        case 'else':
+          if (js && js.trim()) {
+            this.error('`else` cannot have a condition, perhaps you meant `else if`', 'ELSE_CONDITION');
+          }
+          js = 'else';
+          isElse = true;
+          break;
+      }
+      var tok = this.tok('code', js);
+      tok.isElse = isElse;
+      tok.isIf = isIf;
+      tok.requiresBlock = true;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * While.
+   */
+
+  "while": function() {
+    var captures;
+    if (captures = /^while +([^\n]+)/.exec(this.input)) {
+      this.consume(captures[0].length);
+      this.assertExpression(captures[1])
+      var tok = this.tok('code', 'while (' + captures[1] + ')');
+      tok.requiresBlock = true;
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Each.
+   */
+
+  each: function() {
+    var captures;
+    if (captures = /^(?:- *)?(?:each|for) +([a-zA-Z_$][\w$]*)(?: *, *([a-zA-Z_$][\w$]*))? * in *([^\n]+)/.exec(this.input)) {
+      this.consume(captures[0].length);
+      var tok = this.tok('each', captures[1]);
+      tok.key = captures[2];
+      this.assertExpression(captures[3])
+      tok.code = captures[3];
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Code.
+   */
+
+  code: function() {
+    var captures;
+    if (captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input)) {
+      this.consume(captures[0].length);
+      var flags = captures[1];
+      captures[1] = captures[2];
+      var tok = this.tok('code', captures[1]);
+      tok.escape = flags.charAt(0) === '=';
+      tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '=';
+      if (tok.buffer) this.assertExpression(captures[1]);
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  /**
+   * Block code.
+   */
+  blockCode: function() {
+    var captures;
+    if (captures = /^-\n/.exec(this.input)) {
+      this.consume(1);
+      this.tokens.push(this.tok('blockcode'));
+      this.pipeless = true;
+      return true;
+    }
+  },
+
+  /**
+   * Attributes.
+   */
+
+  attrs: function(push) {
+    if ('(' == this.input.charAt(0)) {
+      var index = this.bracketExpression().end
+        , str = this.input.substr(1, index-1)
+        , tok = this.tok('attrs');
+
+      this.assertNestingCorrect(str);
+
+      var quote = '';
+      var self = this;
+      var interpolate = function (attr) {
+        return attr.replace(/(\\)?#\{(.+)/g, function(_, escape, expr){
+          if (escape) return _;
+          var range = characterParser.parseMax(expr);
+          if (expr[range.end] !== '}') return _.substr(0, 2) + interpolate(_.substr(2));
+          self.assertExpression(range.src)
+          return quote + " + (" + range.src + ") + " + quote + interpolate(expr.substr(range.end + 1));
+        });
+      }
+
+      this.consume(index + 1);
+      tok.attrs = [];
+
+      var escapedAttr = true
+      var key = '';
+      var val = '';
+      var interpolatable = '';
+      var state = characterParser.defaultState();
+      var loc = 'key';
+      var isEndOfAttribute = function (i) {
+        if (key.trim() === '') return false;
+        if (i === str.length) return true;
+        if (loc === 'key') {
+          if (str[i] === ' ' || str[i] === '\n') {
+            for (var x = i; x < str.length; x++) {
+              if (str[x] != ' ' && str[x] != '\n') {
+                if (str[x] === '=' || str[x] === '!' || str[x] === ',') return false;
+                else return true;
+              }
+            }
+          }
+          return str[i] === ','
+        } else if (loc === 'value' && !state.isNesting()) {
+          try {
+            self.assertExpression(val);
+            if (str[i] === ' ' || str[i] === '\n') {
+              for (var x = i; x < str.length; x++) {
+                if (str[x] != ' ' && str[x] != '\n') {
+                  if (characterParser.isPunctuator(str[x]) && str[x] != '"' && str[x] != "'") return false;
+                  else return true;
+                }
+              }
+            }
+            return str[i] === ',';
+          } catch (ex) {
+            return false;
+          }
+        }
+      }
+
+      this.lineno += str.split("\n").length - 1;
+
+      for (var i = 0; i <= str.length; i++) {
+        if (isEndOfAttribute(i)) {
+          val = val.trim();
+          if (val) this.assertExpression(val)
+          key = key.trim();
+          key = key.replace(/^['"]|['"]$/g, '');
+          tok.attrs.push({
+            name: key,
+            val: '' == val ? true : val,
+            escaped: escapedAttr
+          });
+          key = val = '';
+          loc = 'key';
+          escapedAttr = false;
+        } else {
+          switch (loc) {
+            case 'key-char':
+              if (str[i] === quote) {
+                loc = 'key';
+                if (i + 1 < str.length && [' ', ',', '!', '=', '\n'].indexOf(str[i + 1]) === -1)
+                  this.error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`', 'INVALID_KEY_CHARACTER');
+              } else {
+                key += str[i];
+              }
+              break;
+            case 'key':
+              if (key === '' && (str[i] === '"' || str[i] === "'")) {
+                loc = 'key-char';
+                quote = str[i];
+              } else if (str[i] === '!' || str[i] === '=') {
+                escapedAttr = str[i] !== '!';
+                if (str[i] === '!') i++;
+                if (str[i] !== '=') this.error('Unexpected character ' + str[i] + ' expected `=`', 'INVALID_KEY_CHARACTER');
+                loc = 'value';
+                state = characterParser.defaultState();
+              } else {
+                key += str[i]
+              }
+              break;
+            case 'value':
+              state = characterParser.parseChar(str[i], state);
+              if (state.isString()) {
+                loc = 'string';
+                quote = str[i];
+                interpolatable = str[i];
+              } else {
+                val += str[i];
+              }
+              break;
+            case 'string':
+              state = characterParser.parseChar(str[i], state);
+              interpolatable += str[i];
+              if (!state.isString()) {
+                loc = 'value';
+                val += interpolate(interpolatable);
+              }
+              break;
+          }
+        }
+      }
+
+      if ('/' == this.input.charAt(0)) {
+        this.consume(1);
+        tok.selfClosing = true;
+      }
+      if (push) {
+        this.tokens.push(tok);
+        return true;
+      }
+      return tok;
+    }
+  },
+
+  /**
+   * &attributes block
+   */
+  attributesBlock: function () {
+    var captures;
+    if (/^&attributes\b/.test(this.input)) {
+      this.consume(11);
+      var args = this.bracketExpression();
+      this.consume(args.end + 1);
+      this.tokens.push(this.tok('&attributes', args.src));
+      return true;
+    }
+  },
+
+  /**
+   * Indent | Outdent | Newline.
+   */
+
+  indent: function() {
+    var captures, re;
+
+    // established regexp
+    if (this.indentRe) {
+      captures = this.indentRe.exec(this.input);
+    // determine regexp
+    } else {
+      // tabs
+      re = /^\n(\t*) */;
+      captures = re.exec(this.input);
+
+      // spaces
+      if (captures && !captures[1].length) {
+        re = /^\n( *)/;
+        captures = re.exec(this.input);
+      }
+
+      // established
+      if (captures && captures[1].length) this.indentRe = re;
+    }
+
+    if (captures) {
+      var tok
+        , indents = captures[1].length;
+
+      ++this.lineno;
+      this.consume(indents + 1);
+
+      if (' ' == this.input[0] || '\t' == this.input[0]) {
+        this.error('Invalid indentation, you can use tabs or spaces but not both', 'INVALID_INDENTATION');
+      }
+
+      // blank line
+      if ('\n' == this.input[0]) {
+        this.pipeless = false;
+        return this.tok('newline');
+      }
+
+      // outdent
+      if (this.indentStack.length && indents < this.indentStack[0]) {
+        while (this.indentStack.length && this.indentStack[0] > indents) {
+          this.tokens.push(this.tok('outdent'));
+          this.indentStack.shift();
+        }
+      // indent
+      } else if (indents && indents != this.indentStack[0]) {
+        this.indentStack.unshift(indents);
+        this.tokens.push(this.tok('indent', indents));
+      // newline
+      } else {
+        this.tokens.push(this.tok('newline'));
+      }
+
+      this.pipeless = false;
+      return true;
+    }
+  },
+
+  /**
+   * Pipe-less text consumed only when
+   * pipeless is true;
+   */
+
+  pipelessText: function() {
+    if (!this.pipeless) return;
+    var captures, re;
+
+    // established regexp
+    if (this.indentRe) {
+      captures = this.indentRe.exec(this.input);
+    // determine regexp
+    } else {
+      // tabs
+      re = /^\n(\t*) */;
+      captures = re.exec(this.input);
+
+      // spaces
+      if (captures && !captures[1].length) {
+        re = /^\n( *)/;
+        captures = re.exec(this.input);
+      }
+
+      // established
+      if (captures && captures[1].length) this.indentRe = re;
+    }
+
+
+    var indents = captures && captures[1].length;
+    if (indents && (this.indentStack.length === 0 || indents > this.indentStack[0])) {
+      this.tokens.push(this.tok('start-pipeless-text'));
+      var indent = captures[1];
+      var tokens = [];
+      var isMatch;
+      do {
+        // text has `\n` as a prefix
+        var i = this.input.substr(1).indexOf('\n');
+        if (-1 == i) i = this.input.length - 1;
+        var str = this.input.substr(1, i);
+        isMatch = str.substr(0, indent.length) === indent || !str.trim();
+        if (isMatch) {
+          // consume test along with `\n` prefix if match
+          this.consume(str.length + 1);
+          tokens.push(str.substr(indent.length));
+        }
+      } while(this.input.length && isMatch);
+      while (this.input.length === 0 && tokens[tokens.length - 1] === '') tokens.pop();
+      tokens.forEach(function (token, i) {
+        this.lineno++;
+        if (i !== 0) this.tokens.push(this.tok('newline'));
+        this.addText(token);
+      }.bind(this));
+      this.tokens.push(this.tok('end-pipeless-text'));
+      return true;
+    }
+  },
+
+  /**
+   * ':'
+   */
+
+  colon: function() {
+    var tok = this.scan(/^: +/, ':');
+    if (tok) {
+      this.tokens.push(tok);
+      return true;
+    }
+  },
+
+  fail: function () {
+    this.error('unexpected text ' + this.input.substr(0, 5), 'UNEXPECTED_TEXT');
+  },
+
+  /**
+   * Move to the next token
+   *
+   * @api private
+   */
+
+  advance: function() {
+    return this.blank()
+      || this.eos()
+      || this.endInterpolation()
+      || this.pipelessText()
+      || this.yield()
+      || this.doctype()
+      || this.interpolation()
+      || this["case"]()
+      || this.when()
+      || this["default"]()
+      || this["extends"]()
+      || this.append()
+      || this.prepend()
+      || this.block()
+      || this.mixinBlock()
+      || this.include()
+      || this.includeFiltered()
+      || this.mixin()
+      || this.call()
+      || this.conditional()
+      || this.each()
+      || this["while"]()
+      || this.tag()
+      || this.filter()
+      || this.blockCode()
+      || this.code()
+      || this.id()
+      || this.className()
+      || this.attrs(true)
+      || this.attributesBlock()
+      || this.indent()
+      || this.text()
+      || this.textHtml()
+      || this.comment()
+      || this.colon()
+      || this.dot()
+      || this.textFail()
+      || this.fail();
+  },
+
+  /**
+   * Return an array of tokens for the current file
+   *
+   * @returns {Array.<Token>}
+   * @api public
+   */
+  getTokens: function () {
+    while (!this.ended) {
+      this.advance();
+    }
+    return this.tokens;
+  }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/.npmignore
new file mode 100644
index 0000000..cefaa67
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/.npmignore
@@ -0,0 +1,2 @@
+test/
+.travis.yml
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/LICENSE
new file mode 100644
index 0000000..e1b32ec
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/README.md
new file mode 100644
index 0000000..02a0b99
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/README.md
@@ -0,0 +1,142 @@
+# character-parser
+
+Parse JavaScript one character at a time to look for snippets in Templates.  This is not a validator, it's just designed to allow you to have sections of JavaScript delimited by brackets robustly.
+
+[![Build Status](https://img.shields.io/travis/ForbesLindesay/character-parser/master.svg)](https://travis-ci.org/ForbesLindesay/character-parser)
+
+## Installation
+
+    npm install character-parser
+
+## Usage
+
+Work out how much depth changes:
+
+```js
+var state = parse('foo(arg1, arg2, {\n  foo: [a, b\n');
+assert(state.roundDepth === 1);
+assert(state.curlyDepth === 1);
+assert(state.squareDepth === 1);
+parse('    c, d]\n  })', state);
+assert(state.squareDepth === 0);
+assert(state.curlyDepth === 0);
+assert(state.roundDepth === 0);
+```
+
+### Bracketed Expressions
+
+Find all the contents of a bracketed expression:
+
+```js
+var section = parser.parseMax('foo="(", bar="}") bing bong');
+assert(section.start === 0);
+assert(section.end === 16);//exclusive end of string
+assert(section.src = 'foo="(", bar="}"');
+
+
+var section = parser.parseMax('{foo="(", bar="}"} bing bong', {start: 1});
+assert(section.start === 1);
+assert(section.end === 17);//exclusive end of string
+assert(section.src = 'foo="(", bar="}"');
+```
+
+The bracketed expression parsing simply parses up to but excluding the first unmatched closed bracket (`)`, `}`, `]`).  It is clever enough to ignore brackets in comments or strings.
+
+
+### Custom Delimited Expressions
+
+Find code up to a custom delimiter:
+
+```js
+var section = parser.parseUntil('foo.bar("%>").baz%> bing bong', '%>');
+assert(section.start === 0);
+assert(section.end === 17);//exclusive end of string
+assert(section.src = 'foo.bar("%>").baz');
+
+var section = parser.parseUntil('<%foo.bar("%>").baz%> bing bong', '%>', {start: 2});
+assert(section.start === 2);
+assert(section.end === 19);//exclusive end of string
+assert(section.src = 'foo.bar("%>").baz');
+```
+
+Delimiters are ignored if they are inside strings or comments.
+
+## API
+
+### parse(str, state = defaultState(), options = {start: 0, end: src.length})
+
+Parse a string starting at the index start, and return the state after parsing that string.
+
+If you want to parse one string in multiple sections you should keep passing the resulting state to the next parse operation.
+
+Returns a `State` object.
+
+### parseMax(src, options = {start: 0})
+
+Parses the source until the first unmatched close bracket (any of `)`, `}`, `]`).  It returns an object with the structure:
+
+```js
+{
+  start: 0,//index of first character of string
+  end: 13,//index of first character after the end of string
+  src: 'source string'
+}
+```
+
+### parseUntil(src, delimiter, options = {start: 0, includeLineComment: false})
+
+Parses the source until the first occurence of `delimiter` which is not in a string or a comment.  If `includeLineComment` is `true`, it will still count if the delimiter occurs in a line comment, but not in a block comment.  It returns an object with the structure:
+
+```js
+{
+  start: 0,//index of first character of string
+  end: 13,//index of first character after the end of string
+  src: 'source string'
+}
+```
+
+### parseChar(character, state = defaultState())
+
+Parses the single character and returns the state.  See `parse` for the structure of the returned state object.  N.B. character must be a single character not a multi character string.
+
+### defaultState()
+
+Get a default starting state.
+
+### isPunctuator(character)
+
+Returns `true` if `character` represents punctuation in JavaScript.
+
+### isKeyword(name)
+
+Returns `true` if `name` is a keyword in JavaScript.
+
+## State
+
+A state is an object with the following structure
+
+```js
+{
+  lineComment: false, //true if inside a line comment
+  blockComment: false, //true if inside a block comment
+
+  singleQuote: false, //true if inside a single quoted string
+  doubleQuote: false, //true if inside a double quoted string
+  regexp:      false, //true if inside a regular expression
+  escaped: false, //true if in a string and the last character was an escape character
+
+  roundDepth: 0, //number of un-closed open `(` brackets
+  curlyDepth: 0, //number of un-closed open `{` brackets
+  squareDepth: 0 //number of un-closed open `[` brackets
+}
+```
+
+It also has the following useful methods:
+
+- `.isString()` returns `true` if the current location is inside a string.
+- `.isComment()` returns `true` if the current location is inside a comment.
+- `isNesting()` returns `true` if the current location is anything but at the top level, i.e. with no nesting.
+
+## License
+
+MIT
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/index.js
new file mode 100644
index 0000000..d5c260b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/index.js
@@ -0,0 +1,230 @@
+exports = (module.exports = parse);
+exports.parse = parse;
+function parse(src, state, options) {
+  options = options || {};
+  state = state || exports.defaultState();
+  var start = options.start || 0;
+  var end = options.end || src.length;
+  var index = start;
+  while (index < end) {
+    if (state.roundDepth < 0 || state.curlyDepth < 0 || state.squareDepth < 0) {
+      throw new SyntaxError('Mismatched Bracket: ' + src[index - 1]);
+    }
+    exports.parseChar(src[index++], state);
+  }
+  return state;
+}
+
+exports.parseMax = parseMax;
+function parseMax(src, options) {
+  options = options || {};
+  var start = options.start || 0;
+  var index = start;
+  var state = exports.defaultState();
+  while (state.roundDepth >= 0 && state.curlyDepth >= 0 && state.squareDepth >= 0) {
+    if (index >= src.length) {
+      throw new Error('The end of the string was reached with no closing bracket found.');
+    }
+    exports.parseChar(src[index++], state);
+  }
+  var end = index - 1;
+  return {
+    start: start,
+    end: end,
+    src: src.substring(start, end)
+  };
+}
+
+exports.parseUntil = parseUntil;
+function parseUntil(src, delimiter, options) {
+  options = options || {};
+  var includeLineComment = options.includeLineComment || false;
+  var start = options.start || 0;
+  var index = start;
+  var state = exports.defaultState();
+  while (state.isString() || state.regexp || state.blockComment ||
+         (!includeLineComment && state.lineComment) || !startsWith(src, delimiter, index)) {
+    exports.parseChar(src[index++], state);
+  }
+  var end = index;
+  return {
+    start: start,
+    end: end,
+    src: src.substring(start, end)
+  };
+}
+
+
+exports.parseChar = parseChar;
+function parseChar(character, state) {
+  if (character.length !== 1) throw new Error('Character must be a string of length 1');
+  state = state || exports.defaultState();
+  state.src = state.src || '';
+  state.src += character;
+  var wasComment = state.blockComment || state.lineComment;
+  var lastChar = state.history ? state.history[0] : '';
+
+  if (state.regexpStart) {
+    if (character === '/' || character == '*') {
+      state.regexp = false;
+    }
+    state.regexpStart = false;
+  }
+  if (state.lineComment) {
+    if (character === '\n') {
+      state.lineComment = false;
+    }
+  } else if (state.blockComment) {
+    if (state.lastChar === '*' && character === '/') {
+      state.blockComment = false;
+    }
+  } else if (state.singleQuote) {
+    if (character === '\'' && !state.escaped) {
+      state.singleQuote = false;
+    } else if (character === '\\' && !state.escaped) {
+      state.escaped = true;
+    } else {
+      state.escaped = false;
+    }
+  } else if (state.doubleQuote) {
+    if (character === '"' && !state.escaped) {
+      state.doubleQuote = false;
+    } else if (character === '\\' && !state.escaped) {
+      state.escaped = true;
+    } else {
+      state.escaped = false;
+    }
+  } else if (state.regexp) {
+    if (character === '/' && !state.escaped) {
+      state.regexp = false;
+    } else if (character === '\\' && !state.escaped) {
+      state.escaped = true;
+    } else {
+      state.escaped = false;
+    }
+  } else if (lastChar === '/' && character === '/') {
+    state.history = state.history.substr(1);
+    state.lineComment = true;
+  } else if (lastChar === '/' && character === '*') {
+    state.history = state.history.substr(1);
+    state.blockComment = true;
+  } else if (character === '/' && isRegexp(state.history)) {
+    state.regexp = true;
+    state.regexpStart = true;
+  } else if (character === '\'') {
+    state.singleQuote = true;
+  } else if (character === '"') {
+    state.doubleQuote = true;
+  } else if (character === '(') {
+    state.roundDepth++;
+  } else if (character === ')') {
+    state.roundDepth--;
+  } else if (character === '{') {
+    state.curlyDepth++;
+  } else if (character === '}') {
+    state.curlyDepth--;
+  } else if (character === '[') {
+    state.squareDepth++;
+  } else if (character === ']') {
+    state.squareDepth--;
+  }
+  if (!state.blockComment && !state.lineComment && !wasComment) state.history = character + state.history;
+  state.lastChar = character; // store last character for ending block comments
+  return state;
+}
+
+exports.defaultState = function () { return new State() };
+function State() {
+  this.lineComment = false;
+  this.blockComment = false;
+
+  this.singleQuote = false;
+  this.doubleQuote = false;
+  this.regexp = false;
+
+  this.escaped = false;
+
+  this.roundDepth = 0;
+  this.curlyDepth = 0;
+  this.squareDepth = 0;
+
+  this.history = ''
+  this.lastChar = ''
+}
+State.prototype.isString = function () {
+  return this.singleQuote || this.doubleQuote;
+}
+State.prototype.isComment = function () {
+  return this.lineComment || this.blockComment;
+}
+State.prototype.isNesting = function () {
+  return this.isString() || this.isComment() || this.regexp || this.roundDepth > 0 || this.curlyDepth > 0 || this.squareDepth > 0
+}
+
+function startsWith(str, start, i) {
+  return str.substr(i || 0, start.length) === start;
+}
+
+exports.isPunctuator = isPunctuator
+function isPunctuator(c) {
+  if (!c) return true; // the start of a string is a punctuator
+  var code = c.charCodeAt(0)
+
+  switch (code) {
+    case 46:   // . dot
+    case 40:   // ( open bracket
+    case 41:   // ) close bracket
+    case 59:   // ; semicolon
+    case 44:   // , comma
+    case 123:  // { open curly brace
+    case 125:  // } close curly brace
+    case 91:   // [
+    case 93:   // ]
+    case 58:   // :
+    case 63:   // ?
+    case 126:  // ~
+    case 37:   // %
+    case 38:   // &
+    case 42:   // *:
+    case 43:   // +
+    case 45:   // -
+    case 47:   // /
+    case 60:   // <
+    case 62:   // >
+    case 94:   // ^
+    case 124:  // |
+    case 33:   // !
+    case 61:   // =
+      return true;
+    default:
+      return false;
+  }
+}
+exports.isKeyword = isKeyword
+function isKeyword(id) {
+  return (id === 'if') || (id === 'in') || (id === 'do') || (id === 'var') || (id === 'for') || (id === 'new') ||
+         (id === 'try') || (id === 'let') || (id === 'this') || (id === 'else') || (id === 'case') ||
+         (id === 'void') || (id === 'with') || (id === 'enum') || (id === 'while') || (id === 'break') || (id === 'catch') ||
+         (id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super') ||
+         (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') ||
+         (id === 'import') || (id === 'default') || (id === 'finally') || (id === 'extends') || (id === 'function') ||
+         (id === 'continue') || (id === 'debugger') || (id === 'package') || (id === 'private') || (id === 'interface') ||
+         (id === 'instanceof') || (id === 'implements') || (id === 'protected') || (id === 'public') || (id === 'static');
+}
+
+function isRegexp(history) {
+  //could be start of regexp or divide sign
+
+  history = history.replace(/^\s*/, '');
+
+  //unless its an `if`, `while`, `for` or `with` it's a divide, so we assume it's a divide
+  if (history[0] === ')') return false;
+  //unless it's a function expression, it's a regexp, so we assume it's a regexp
+  if (history[0] === '}') return true;
+  //any punctuation means it's a regexp
+  if (isPunctuator(history[0])) return true;
+  //if the last thing was a keyword then it must be a regexp (e.g. `typeof /foo/`)
+  if (/^\w+\b/.test(history) && isKeyword(/^\w+\b/.exec(history)[0].split('').reverse().join(''))) return true;
+
+  return false;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/package.json
new file mode 100644
index 0000000..fd5bbba
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/node_modules/character-parser/package.json
@@ -0,0 +1,56 @@
+{
+  "name": "character-parser",
+  "version": "1.2.2",
+  "description": "Parse JavaScript one character at a time to look for snippets in Templates.  This is not a validator, it's just designed to allow you to have sections of JavaScript delimited by brackets robustly.",
+  "main": "index.js",
+  "scripts": {
+    "test": "mocha -R spec"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/ForbesLindesay/character-parser.git"
+  },
+  "keywords": [
+    "parser",
+    "JavaScript",
+    "bracket",
+    "nesting",
+    "comment",
+    "string",
+    "escape",
+    "escaping"
+  ],
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "devDependencies": {
+    "better-assert": "~1.0.0",
+    "mocha": "~1.9.0"
+  },
+  "gitHead": "cfd4c8ccbb90a52646594377425b805c8bebc482",
+  "bugs": {
+    "url": "https://github.com/ForbesLindesay/character-parser/issues"
+  },
+  "homepage": "https://github.com/ForbesLindesay/character-parser",
+  "_id": "character-parser@1.2.2",
+  "_shasum": "92d3dd48bfdc9ad5e913ec953d90f6d571a467d3",
+  "_from": "character-parser@>=1.2.1 <2.0.0",
+  "_npmVersion": "1.4.28",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindeay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "92d3dd48bfdc9ad5e913ec953d90f6d571a467d3",
+    "tarball": "http://registry.npmjs.org/character-parser/-/character-parser-1.2.2.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.2.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/package.json
new file mode 100644
index 0000000..bcb3ecf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/package.json
@@ -0,0 +1,51 @@
+{
+  "name": "jade-lexer",
+  "version": "0.0.8",
+  "description": "The jade lexer (takes a string and converts it to an array of tokens)",
+  "keywords": [],
+  "dependencies": {
+    "character-parser": "^1.2.1"
+  },
+  "devDependencies": {
+    "istanbul": "^0.3.2"
+  },
+  "scripts": {
+    "update-test-cases": "node test/update-test-cases",
+    "test": "node test && npm run coverage",
+    "coverage": "istanbul cover test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-lexer.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "4267ded89c286febb939e81f75c13ebd08448153",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-lexer/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-lexer",
+  "_id": "jade-lexer@0.0.8",
+  "_shasum": "5a9164a0e0d2c0063507b54c7b1642c7ae11b7a4",
+  "_from": "jade-lexer@*",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "5a9164a0e0d2c0063507b54c7b1642c7ae11b7a4",
+    "tarball": "http://registry.npmjs.org/jade-lexer/-/jade-lexer-0.0.8.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-lexer/-/jade-lexer-0.0.8.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs-data.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs-data.expected.json
new file mode 100644
index 0000000..cc05f84
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs-data.expected.json
@@ -0,0 +1,15 @@
+{"type":"code","line":1,"val":"var user = { name: 'tobi' }","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"foo","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"data-user","val":"user","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"data-items","val":"[1,2,3]","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"foo","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"data-username","val":"'tobi'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"foo","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"data-escaped","val":"{message: \"Let's rock!\"}","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs-data.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs-data.jade
new file mode 100644
index 0000000..4e47db4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs-data.jade
@@ -0,0 +1,5 @@
+- var user = { name: 'tobi' }
+foo(data-user=user)
+foo(data-items=[1,2,3])
+foo(data-username='tobi')
+foo(data-escaped={message: "Let's rock!"})
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.expected.json
new file mode 100644
index 0000000..39050d7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.expected.json
@@ -0,0 +1,77 @@
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"href","val":"'/contact'","escaped":true}]}
+{"type":"text","line":1,"val":"contact"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'/save'","escaped":true}]}
+{"type":"class","line":2,"val":"button"}
+{"type":"text","line":2,"val":"save"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"foo","val":true,"escaped":true},{"name":"bar","val":true,"escaped":false},{"name":"baz","val":true,"escaped":false}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"a","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"foo","val":"'foo, bar, baz'","escaped":true},{"name":"bar","val":"1","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"a","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"foo","val":"'((foo))'","escaped":true},{"name":"bar","val":"(1) ? 1 : 0","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"select","selfClosing":false}
+{"type":"indent","line":7,"val":2}
+{"type":"tag","line":7,"val":"option","selfClosing":false}
+{"type":"attrs","line":7,"attrs":[{"name":"value","val":"'foo'","escaped":true},{"name":"selected","val":true,"escaped":false}]}
+{"type":"text","line":7,"val":"Foo"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"option","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"selected","val":true,"escaped":true},{"name":"value","val":"'bar'","escaped":true}]}
+{"type":"text","line":8,"val":"Bar"}
+{"type":"outdent","line":9}
+{"type":"tag","line":9,"val":"a","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"foo","val":"\"class:\"","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"input","selfClosing":false}
+{"type":"attrs","line":10,"attrs":[{"name":"pattern","val":"'\\\\S+'","escaped":true}]}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"a","selfClosing":false}
+{"type":"attrs","line":12,"attrs":[{"name":"href","val":"'/contact'","escaped":true}]}
+{"type":"text","line":12,"val":"contact"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"a","selfClosing":false}
+{"type":"attrs","line":13,"attrs":[{"name":"href","val":"'/save'","escaped":true}]}
+{"type":"class","line":13,"val":"button"}
+{"type":"text","line":13,"val":"save"}
+{"type":"newline","line":14}
+{"type":"tag","line":14,"val":"a","selfClosing":false}
+{"type":"attrs","line":14,"attrs":[{"name":"foo","val":true,"escaped":true},{"name":"bar","val":true,"escaped":false},{"name":"baz","val":true,"escaped":false}]}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"a","selfClosing":false}
+{"type":"attrs","line":15,"attrs":[{"name":"foo","val":"'foo, bar, baz'","escaped":true},{"name":"bar","val":"1","escaped":true}]}
+{"type":"newline","line":16}
+{"type":"tag","line":16,"val":"a","selfClosing":false}
+{"type":"attrs","line":16,"attrs":[{"name":"foo","val":"'((foo))'","escaped":true},{"name":"bar","val":"(1) ? 1 : 0","escaped":true}]}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"select","selfClosing":false}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"option","selfClosing":false}
+{"type":"attrs","line":18,"attrs":[{"name":"value","val":"'foo'","escaped":true},{"name":"selected","val":true,"escaped":false}]}
+{"type":"text","line":18,"val":"Foo"}
+{"type":"newline","line":19}
+{"type":"tag","line":19,"val":"option","selfClosing":false}
+{"type":"attrs","line":19,"attrs":[{"name":"selected","val":true,"escaped":true},{"name":"value","val":"'bar'","escaped":true}]}
+{"type":"text","line":19,"val":"Bar"}
+{"type":"outdent","line":20}
+{"type":"tag","line":20,"val":"a","selfClosing":false}
+{"type":"attrs","line":20,"attrs":[{"name":"foo","val":"\"class:\"","escaped":true}]}
+{"type":"newline","line":21}
+{"type":"tag","line":21,"val":"input","selfClosing":false}
+{"type":"attrs","line":21,"attrs":[{"name":"pattern","val":"'\\\\S+'","escaped":true}]}
+{"type":"newline","line":22}
+{"type":"tag","line":22,"val":"foo","selfClosing":false}
+{"type":"attrs","line":22,"attrs":[{"name":"terse","val":"\"true\"","escaped":true}]}
+{"type":"newline","line":24}
+{"type":"code","line":24,"val":"var attrs = {foo: 'bar', bar: '<baz>'}","escape":false,"buffer":false}
+{"type":"newline","line":26}
+{"type":"tag","line":26,"val":"div","selfClosing":false}
+{"type":"&attributes","line":26,"val":"attrs"}
+{"type":"newline","line":27}
+{"type":"eos","line":27}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.interpolation.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.interpolation.expected.json
new file mode 100644
index 0000000..a7ccb4b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.interpolation.expected.json
@@ -0,0 +1,8 @@
+{"type":"code","line":1,"val":"var id = 5","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'/user/' + (id) + ''","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"bar","val":"'stuff \\#{here} yup'","escaped":true}]}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.interpolation.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.interpolation.jade
new file mode 100644
index 0000000..ae1ea2b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.interpolation.jade
@@ -0,0 +1,3 @@
+- var id = 5
+a(href='/user/#{id}')
+foo(bar='stuff \#{here} yup')
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.jade
new file mode 100644
index 0000000..7bd76c2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.jade
@@ -0,0 +1,26 @@
+a(href='/contact') contact
+a(href='/save').button save
+a(foo, bar, baz)
+a(foo='foo, bar, baz', bar=1)
+a(foo='((foo))', bar= (1) ? 1 : 0 )
+select
+  option(value='foo', selected) Foo
+  option(selected, value='bar') Bar
+a(foo="class:")
+input(pattern='\\S+')
+
+a(href='/contact') contact
+a(href='/save').button save
+a(foo bar baz)
+a(foo='foo, bar, baz' bar=1)
+a(foo='((foo))' bar= (1) ? 1 : 0 )
+select
+  option(value='foo' selected) Foo
+  option(selected value='bar') Bar
+a(foo="class:")
+input(pattern='\\S+')
+foo(terse="true")
+
+- var attrs = {foo: 'bar', bar: '<baz>'}
+
+div&attributes(attrs)
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.js.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.js.expected.json
new file mode 100644
index 0000000..48eefb3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.js.expected.json
@@ -0,0 +1,47 @@
+{"type":"code","line":1,"val":"var id = 5","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"function answer() { return 42; }","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"a","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"meta","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"key","val":"'answer'","escaped":true},{"name":"value","val":"answer()","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"a","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"a","selfClosing":false}
+{"type":"class","line":7,"val":"tag-class"}
+{"type":"attrs","line":7,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"a","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"a","selfClosing":false}
+{"type":"attrs","line":10,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"meta","selfClosing":false}
+{"type":"attrs","line":11,"attrs":[{"name":"key","val":"'answer'","escaped":true},{"name":"value","val":"answer()","escaped":true}]}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"a","selfClosing":false}
+{"type":"attrs","line":12,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"a","selfClosing":false}
+{"type":"class","line":13,"val":"tag-class"}
+{"type":"attrs","line":13,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"div","selfClosing":false}
+{"type":"attrs","line":15,"attrs":[{"name":"id","val":"id","escaped":true}]}
+{"type":"&attributes","line":15,"val":"{foo: 'bar'}"}
+{"type":"newline","line":16}
+{"type":"code","line":16,"val":"var bar = null","escape":false,"buffer":false}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"div","selfClosing":false}
+{"type":"attrs","line":17,"attrs":[{"name":"foo","val":"null","escaped":true},{"name":"bar","val":"bar","escaped":true}]}
+{"type":"&attributes","line":17,"val":"{baz: 'baz'}"}
+{"type":"newline","line":18}
+{"type":"eos","line":18}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.js.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.js.jade
new file mode 100644
index 0000000..910c13a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.js.jade
@@ -0,0 +1,17 @@
+- var id = 5
+- function answer() { return 42; }
+a(href='/user/' + id, class='button')
+a(href  =  '/user/' + id, class  =  'button')
+meta(key='answer', value=answer())
+a(class = ['class1', 'class2'])
+a.tag-class(class = ['class1', 'class2'])
+
+a(href='/user/' + id class='button')
+a(href  =  '/user/' + id class  =  'button')
+meta(key='answer' value=answer())
+a(class = ['class1', 'class2'])
+a.tag-class(class = ['class1', 'class2'])
+
+div(id=id)&attributes({foo: 'bar'})
+- var bar = null
+div(foo=null bar=bar)&attributes({baz: 'baz'})
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.unescaped.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.unescaped.expected.json
new file mode 100644
index 0000000..1ef6841
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.unescaped.expected.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"div","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"id","val":"'user-<%= user.id %>'","escaped":false}]}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"text","line":3,"val":"<%= user.title %>"}
+{"type":"outdent","line":3}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.unescaped.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.unescaped.jade
new file mode 100644
index 0000000..36a4e10
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/attrs.unescaped.jade
@@ -0,0 +1,3 @@
+script(type='text/x-template')
+  div(id!='user-<%= user.id %>')
+    h1 <%= user.title %>
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/basic.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/basic.expected.json
new file mode 100644
index 0000000..9fcab14
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/basic.expected.json
@@ -0,0 +1,9 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"text","line":3,"val":"Title"}
+{"type":"outdent","line":3}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/basic.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/basic.jade
new file mode 100644
index 0000000..77066d1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/basic.jade
@@ -0,0 +1,3 @@
+html
+  body
+    h1 Title
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blanks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blanks.expected.json
new file mode 100644
index 0000000..c685c8d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blanks.expected.json
@@ -0,0 +1,13 @@
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"li","selfClosing":false}
+{"type":"text","line":4,"val":"foo"}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"text","line":6,"val":"bar"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"li","selfClosing":false}
+{"type":"text","line":8,"val":"baz"}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blanks.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blanks.jade
new file mode 100644
index 0000000..67b0697
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blanks.jade
@@ -0,0 +1,8 @@
+
+
+ul
+  li foo
+
+  li bar
+      
+  li baz
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-code.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-code.expected.json
new file mode 100644
index 0000000..52d48a9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-code.expected.json
@@ -0,0 +1,28 @@
+{"type":"blockcode","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"list = [\"uno\", \"dos\", \"tres\","}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"        \"cuatro\", \"cinco\", \"seis\"];"}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"comment","line":4,"val":" Without a block, the element is accepted and no code is generated","buffer":false}
+{"type":"newline","line":5}
+{"type":"blockcode","line":5}
+{"type":"newline","line":6}
+{"type":"each","line":6,"val":"item","code":"list"}
+{"type":"indent","line":7,"val":2}
+{"type":"blockcode","line":7}
+{"type":"start-pipeless-text","line":7}
+{"type":"text","line":8,"val":"string = item.charAt(0)"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":""}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":"  .toUpperCase() +"}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"item.slice(1);"}
+{"type":"end-pipeless-text","line":11}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"li","selfClosing":false}
+{"type":"code","line":12,"val":"string","escape":true,"buffer":true}
+{"type":"outdent","line":12}
+{"type":"eos","line":12}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-code.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-code.jade
new file mode 100644
index 0000000..cea5236
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-code.jade
@@ -0,0 +1,12 @@
+-
+  list = ["uno", "dos", "tres",
+          "cuatro", "cinco", "seis"];
+//- Without a block, the element is accepted and no code is generated
+-
+each item in list
+  -
+    string = item.charAt(0)
+    
+      .toUpperCase() +
+    item.slice(1);
+  li= string
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.expected.json
new file mode 100644
index 0000000..0a00fc8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.expected.json
@@ -0,0 +1,17 @@
+{"type":"tag","line":1,"val":"ul","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"li","selfClosing":false}
+{"type":":","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'#'","escaped":true}]}
+{"type":"text","line":2,"val":"foo"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"li","selfClosing":false}
+{"type":":","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"href","val":"'#'","escaped":true}]}
+{"type":"text","line":3,"val":"bar"}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"text","line":5,"val":"baz"}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.jade
new file mode 100644
index 0000000..fb40f9a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.jade
@@ -0,0 +1,5 @@
+ul
+  li: a(href='#') foo
+  li: a(href='#') bar
+
+p baz
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.shorthands.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.shorthands.expected.json
new file mode 100644
index 0000000..359ab89
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.shorthands.expected.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"ul","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"li","selfClosing":false}
+{"type":"class","line":2,"val":"list-item"}
+{"type":":","line":2}
+{"type":"class","line":2,"val":"foo"}
+{"type":":","line":2}
+{"type":"id","line":2,"val":"bar"}
+{"type":"text","line":2,"val":"baz"}
+{"type":"outdent","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.shorthands.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.shorthands.jade
new file mode 100644
index 0000000..c52a126
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/block-expansion.shorthands.jade
@@ -0,0 +1,2 @@
+ul
+  li.list-item: .foo: #bar baz
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blockquote.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blockquote.expected.json
new file mode 100644
index 0000000..0632a2e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blockquote.expected.json
@@ -0,0 +1,10 @@
+{"type":"tag","line":1,"val":"figure","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"blockquote","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"text","line":3,"val":"Try to define yourself by what you do, and you&#8217;ll burnout every time. You are. That is enough. I rest in that."}
+{"type":"outdent","line":4}
+{"type":"tag","line":4,"val":"figcaption","selfClosing":false}
+{"type":"text","line":4,"val":"from @thefray at 1:43pm on May 10"}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blockquote.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blockquote.jade
new file mode 100644
index 0000000..a23b70f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blockquote.jade
@@ -0,0 +1,4 @@
+figure
+  blockquote
+    | Try to define yourself by what you do, and you&#8217;ll burnout every time. You are. That is enough. I rest in that.
+  figcaption from @thefray at 1:43pm on May 10
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-blocks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-blocks.expected.json
new file mode 100644
index 0000000..7a93d38
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-blocks.expected.json
@@ -0,0 +1,8 @@
+{"type":"extends","line":1,"val":"./auxiliary/blocks-in-blocks-layout.jade"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"body","mode":"replace"}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"text","line":4,"val":"Page 2"}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-blocks.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-blocks.jade
new file mode 100644
index 0000000..3e9a4ad
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-blocks.jade
@@ -0,0 +1,4 @@
+extends ./auxiliary/blocks-in-blocks-layout.jade
+
+block body
+  h1 Page 2
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-if.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-if.expected.json
new file mode 100644
index 0000000..9a2e87e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-if.expected.json
@@ -0,0 +1,43 @@
+{"type":"comment","line":1,"val":" see https://github.com/jadejs/jade/issues/1589","buffer":false}
+{"type":"text","line":2,"val":""}
+{"type":"newline","line":3}
+{"type":"code","line":3,"val":"var ajax = true","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"code","line":5,"val":"if( ajax )","escape":false,"buffer":false}
+{"type":"indent","line":6,"val":4}
+{"type":"comment","line":6,"val":" return only contents if ajax requests","buffer":false}
+{"type":"newline","line":7}
+{"type":"block","line":7,"val":"contents","mode":"replace"}
+{"type":"indent","line":8,"val":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"ajax contents"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"code","line":10,"val":"else","escape":false,"buffer":false}
+{"type":"indent","line":11,"val":4}
+{"type":"comment","line":11,"val":" return all html","buffer":false}
+{"type":"newline","line":12}
+{"type":"doctype","line":12,"val":"html"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"html","selfClosing":false}
+{"type":"indent","line":14,"val":8}
+{"type":"tag","line":14,"val":"head","selfClosing":false}
+{"type":"indent","line":15,"val":12}
+{"type":"tag","line":15,"val":"meta","selfClosing":false}
+{"type":"attrs","line":15,"attrs":[{"name":"charset","val":"'utf8'","escaped":true}]}
+{"type":"newline","line":16}
+{"type":"tag","line":16,"val":"title","selfClosing":false}
+{"type":"text","line":16,"val":"sample"}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"body","selfClosing":false}
+{"type":"indent","line":18,"val":16}
+{"type":"block","line":18,"val":"contents","mode":"replace"}
+{"type":"indent","line":19,"val":20}
+{"type":"tag","line":19,"val":"p","selfClosing":false}
+{"type":"text","line":19,"val":"all contetns"}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"eos","line":20}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-if.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-if.jade
new file mode 100644
index 0000000..b1107b6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/blocks-in-if.jade
@@ -0,0 +1,19 @@
+//- see https://github.com/jadejs/jade/issues/1589
+
+-var ajax = true
+
+-if( ajax )
+    //- return only contents if ajax requests
+    block contents
+        p ajax contents
+
+-else
+    //- return all html
+    doctype html
+    html
+        head
+            meta( charset='utf8' )
+            title sample
+            body
+                block contents
+                    p all contetns
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case-blocks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case-blocks.expected.json
new file mode 100644
index 0000000..958cbf8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case-blocks.expected.json
@@ -0,0 +1,27 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"code","line":3,"val":"var friends = 1","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"case","line":4,"val":"friends"}
+{"type":"indent","line":5,"val":6}
+{"type":"when","line":5,"val":"0"}
+{"type":"indent","line":6,"val":8}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"text","line":6,"val":"you have no friends"}
+{"type":"outdent","line":7}
+{"type":"when","line":7,"val":"1"}
+{"type":"indent","line":8,"val":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"you have a friend"}
+{"type":"outdent","line":9}
+{"type":"default","line":9}
+{"type":"indent","line":10,"val":8}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"you have #{friends} friends"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case-blocks.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case-blocks.jade
new file mode 100644
index 0000000..345cd41
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case-blocks.jade
@@ -0,0 +1,10 @@
+html
+  body
+    - var friends = 1
+    case friends
+      when 0
+        p you have no friends
+      when 1
+        p you have a friend
+      default
+        p you have #{friends} friends
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case.expected.json
new file mode 100644
index 0000000..fdc7d4c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case.expected.json
@@ -0,0 +1,43 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"code","line":3,"val":"var friends = 1","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"case","line":4,"val":"friends"}
+{"type":"indent","line":5,"val":6}
+{"type":"when","line":5,"val":"0"}
+{"type":":","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"text","line":5,"val":"you have no friends"}
+{"type":"newline","line":6}
+{"type":"when","line":6,"val":"1"}
+{"type":":","line":6}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"text","line":6,"val":"you have a friend"}
+{"type":"newline","line":7}
+{"type":"default","line":7}
+{"type":":","line":7}
+{"type":"tag","line":7,"val":"p","selfClosing":false}
+{"type":"text","line":7,"val":"you have #{friends} friends"}
+{"type":"outdent","line":8}
+{"type":"code","line":8,"val":"var friends = 0","escape":false,"buffer":false}
+{"type":"newline","line":9}
+{"type":"case","line":9,"val":"friends"}
+{"type":"indent","line":10,"val":6}
+{"type":"when","line":10,"val":"0"}
+{"type":"newline","line":11}
+{"type":"when","line":11,"val":"1"}
+{"type":"indent","line":12,"val":8}
+{"type":"tag","line":12,"val":"p","selfClosing":false}
+{"type":"text","line":12,"val":"you have very few friends"}
+{"type":"outdent","line":13}
+{"type":"default","line":13}
+{"type":"indent","line":14,"val":8}
+{"type":"tag","line":14,"val":"p","selfClosing":false}
+{"type":"text","line":14,"val":"you have #{friends} friends"}
+{"type":"outdent","line":14}
+{"type":"outdent","line":14}
+{"type":"outdent","line":14}
+{"type":"outdent","line":14}
+{"type":"eos","line":14}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case.jade
new file mode 100644
index 0000000..a14a8c5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/case.jade
@@ -0,0 +1,14 @@
+html
+  body
+    - var friends = 1
+    case friends
+      when 0: p you have no friends
+      when 1: p you have a friend
+      default: p you have #{friends} friends
+    - var friends = 0
+    case friends
+      when 0
+      when 1
+        p you have very few friends
+      default
+        p you have #{friends} friends
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes-empty.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes-empty.expected.json
new file mode 100644
index 0000000..da3dd3c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes-empty.expected.json
@@ -0,0 +1,9 @@
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"class","val":"''","escaped":true}]}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"class","val":"null","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"class","val":"undefined","escaped":true}]}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes-empty.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes-empty.jade
new file mode 100644
index 0000000..5e66d84
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes-empty.jade
@@ -0,0 +1,3 @@
+a(class='')
+a(class=null)
+a(class=undefined)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes.expected.json
new file mode 100644
index 0000000..ac2f243
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes.expected.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"class","val":"['foo', 'bar', 'baz']","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"a","selfClosing":false}
+{"type":"class","line":5,"val":"foo"}
+{"type":"attrs","line":5,"attrs":[{"name":"class","val":"'bar'","escaped":true}]}
+{"type":"class","line":5,"val":"baz"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"a","selfClosing":false}
+{"type":"class","line":9,"val":"foo-bar_baz"}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"a","selfClosing":false}
+{"type":"attrs","line":11,"attrs":[{"name":"class","val":"{foo: true, bar: false, baz: true}","escaped":true}]}
+{"type":"newline","line":12}
+{"type":"eos","line":12}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes.jade
new file mode 100644
index 0000000..67e1a1b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/classes.jade
@@ -0,0 +1,11 @@
+a(class=['foo', 'bar', 'baz'])
+
+
+
+a.foo(class='bar').baz
+
+
+
+a.foo-bar_baz
+
+a(class={foo: true, bar: false, baz: true})
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.conditionals.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.conditionals.expected.json
new file mode 100644
index 0000000..d3daa1c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.conditionals.expected.json
@@ -0,0 +1,73 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"if (true)","escape":false,"buffer":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"text","line":3,"val":"foo"}
+{"type":"outdent","line":4}
+{"type":"code","line":4,"val":"else","escape":false,"buffer":false}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"text","line":5,"val":"bar"}
+{"type":"outdent","line":7}
+{"type":"code","line":7,"val":"if (true) {","escape":false,"buffer":false}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"foo"}
+{"type":"outdent","line":9}
+{"type":"code","line":9,"val":"} else {","escape":false,"buffer":false}
+{"type":"indent","line":10,"val":2}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"bar"}
+{"type":"outdent","line":11}
+{"type":"code","line":11,"val":"}","escape":false,"buffer":false}
+{"type":"newline","line":13}
+{"type":"code","line":13,"val":"if ( true)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":14,"val":2}
+{"type":"tag","line":14,"val":"p","selfClosing":false}
+{"type":"text","line":14,"val":"foo"}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"p","selfClosing":false}
+{"type":"text","line":15,"val":"bar"}
+{"type":"newline","line":16}
+{"type":"tag","line":16,"val":"p","selfClosing":false}
+{"type":"text","line":16,"val":"baz"}
+{"type":"outdent","line":17}
+{"type":"code","line":17,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"p","selfClosing":false}
+{"type":"text","line":18,"val":"bar"}
+{"type":"outdent","line":20}
+{"type":"code","line":20,"val":"if (!( true))","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":21,"val":2}
+{"type":"tag","line":21,"val":"p","selfClosing":false}
+{"type":"text","line":21,"val":"foo"}
+{"type":"outdent","line":22}
+{"type":"code","line":22,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":23,"val":2}
+{"type":"tag","line":23,"val":"p","selfClosing":false}
+{"type":"text","line":23,"val":"bar"}
+{"type":"outdent","line":25}
+{"type":"code","line":25,"val":"if ( 'nested')","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":26,"val":2}
+{"type":"code","line":26,"val":"if ( 'works')","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":27,"val":4}
+{"type":"tag","line":27,"val":"p","selfClosing":false}
+{"type":"text","line":27,"val":"yay"}
+{"type":"outdent","line":29}
+{"type":"outdent","line":29}
+{"type":"comment","line":29,"val":" allow empty blocks","buffer":false}
+{"type":"newline","line":30}
+{"type":"code","line":30,"val":"if ( false)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"newline","line":31}
+{"type":"code","line":31,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":32,"val":2}
+{"type":"class","line":32,"val":"bar"}
+{"type":"outdent","line":33}
+{"type":"code","line":33,"val":"if ( true)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":34,"val":2}
+{"type":"class","line":34,"val":"bar"}
+{"type":"outdent","line":35}
+{"type":"code","line":35,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"newline","line":36}
+{"type":"class","line":36,"val":"bing"}
+{"type":"eos","line":36}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.conditionals.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.conditionals.jade
new file mode 100644
index 0000000..697d727
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.conditionals.jade
@@ -0,0 +1,36 @@
+
+- if (true)
+  p foo
+- else
+  p bar
+
+- if (true) {
+  p foo
+- } else {
+  p bar
+- }
+
+if true
+  p foo
+  p bar
+  p baz
+else
+  p bar
+
+unless true
+  p foo
+else
+  p bar
+
+if 'nested'
+  if 'works'
+    p yay
+
+//- allow empty blocks
+if false
+else
+  .bar
+if true
+  .bar
+else
+.bing
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.escape.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.escape.expected.json
new file mode 100644
index 0000000..f6a4ecf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.escape.expected.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"code","line":1,"val":"'<script>'","escape":true,"buffer":true}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"code","line":2,"val":"'<script>'","escape":false,"buffer":true}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.escape.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.escape.jade
new file mode 100644
index 0000000..762c089
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.escape.jade
@@ -0,0 +1,2 @@
+p= '<script>'
+p!= '<script>'
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.expected.json
new file mode 100644
index 0000000..eb6265a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.expected.json
@@ -0,0 +1,30 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"code","line":1,"val":"null","escape":true,"buffer":true}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"code","line":2,"val":"undefined","escape":true,"buffer":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"code","line":3,"val":"''","escape":true,"buffer":true}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"code","line":4,"val":"0","escape":true,"buffer":true}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"code","line":5,"val":"false","escape":true,"buffer":true}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"foo","val":"null","escaped":true}]}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"p","selfClosing":false}
+{"type":"attrs","line":7,"attrs":[{"name":"foo","val":"undefined","escaped":true}]}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"foo","val":"''","escaped":true}]}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"foo","val":"0","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"attrs","line":10,"attrs":[{"name":"foo","val":"false","escaped":true}]}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.iteration.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.iteration.expected.json
new file mode 100644
index 0000000..76b1dbf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.iteration.expected.json
@@ -0,0 +1,67 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var items = [1,2,3]","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"ul","selfClosing":false}
+{"type":"indent","line":5,"val":2}
+{"type":"code","line":5,"val":"items.forEach(function(item){","escape":false,"buffer":false}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"code","line":6,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"code","line":7,"val":"})","escape":false,"buffer":false}
+{"type":"outdent","line":9}
+{"type":"code","line":9,"val":"var items = [1,2,3]","escape":false,"buffer":false}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"ul","selfClosing":false}
+{"type":"indent","line":12,"val":2}
+{"type":"each","line":12,"val":"item","key":"i","code":"items"}
+{"type":"indent","line":13,"val":4}
+{"type":"tag","line":13,"val":"li","selfClosing":false}
+{"type":"attrs","line":13,"attrs":[{"name":"class","val":"'item-' + (i) + ''","escaped":true}]}
+{"type":"code","line":13,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"tag","line":15,"val":"ul","selfClosing":false}
+{"type":"indent","line":16,"val":2}
+{"type":"each","line":16,"val":"item","key":"i","code":"items"}
+{"type":"indent","line":17,"val":4}
+{"type":"tag","line":17,"val":"li","selfClosing":false}
+{"type":"code","line":17,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":19}
+{"type":"outdent","line":19}
+{"type":"tag","line":19,"val":"ul","selfClosing":false}
+{"type":"indent","line":20,"val":2}
+{"type":"each","line":20,"val":"$item","code":"items"}
+{"type":"indent","line":21,"val":4}
+{"type":"tag","line":21,"val":"li","selfClosing":false}
+{"type":"code","line":21,"val":"$item","escape":true,"buffer":true}
+{"type":"outdent","line":23}
+{"type":"outdent","line":23}
+{"type":"code","line":23,"val":"var nums = [1, 2, 3]","escape":false,"buffer":false}
+{"type":"newline","line":24}
+{"type":"code","line":24,"val":"var letters = ['a', 'b', 'c']","escape":false,"buffer":false}
+{"type":"newline","line":26}
+{"type":"tag","line":26,"val":"ul","selfClosing":false}
+{"type":"indent","line":27,"val":2}
+{"type":"each","line":27,"val":"l","code":"letters"}
+{"type":"indent","line":28,"val":4}
+{"type":"each","line":28,"val":"n","code":"nums"}
+{"type":"indent","line":29,"val":6}
+{"type":"tag","line":29,"val":"li","selfClosing":false}
+{"type":"text","line":29,"val":"#{n}: #{l}"}
+{"type":"outdent","line":31}
+{"type":"outdent","line":31}
+{"type":"outdent","line":31}
+{"type":"code","line":31,"val":"var count = 1","escape":false,"buffer":false}
+{"type":"newline","line":32}
+{"type":"code","line":32,"val":"var counter = function() { return [count++, count++, count++] }","escape":false,"buffer":false}
+{"type":"newline","line":33}
+{"type":"tag","line":33,"val":"ul","selfClosing":false}
+{"type":"indent","line":34,"val":2}
+{"type":"each","line":34,"val":"n","code":"counter()"}
+{"type":"indent","line":35,"val":4}
+{"type":"tag","line":35,"val":"li","selfClosing":false}
+{"type":"text","line":35,"val":"#{n}"}
+{"type":"outdent","line":35}
+{"type":"outdent","line":35}
+{"type":"eos","line":35}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.iteration.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.iteration.jade
new file mode 100644
index 0000000..6065ef1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.iteration.jade
@@ -0,0 +1,35 @@
+
+- var items = [1,2,3]
+
+ul
+  - items.forEach(function(item){
+    li= item
+  - })
+
+- var items = [1,2,3]
+
+ul
+  for item, i in items
+    li(class='item-#{i}')= item
+
+ul
+  each item, i in items
+    li= item
+
+ul
+  each $item in items
+    li= $item
+
+- var nums = [1, 2, 3]
+- var letters = ['a', 'b', 'c']
+
+ul
+  for l in letters
+    for n in nums
+      li #{n}: #{l}
+
+- var count = 1
+- var counter = function() { return [count++, count++, count++] }
+ul
+  for n in counter()
+    li #{n}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.jade
new file mode 100644
index 0000000..aa794a1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/code.jade
@@ -0,0 +1,10 @@
+p= null
+p= undefined
+p= ''
+p= 0
+p= false
+p(foo=null)
+p(foo=undefined)
+p(foo='')
+p(foo=0)
+p(foo=false)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.expected.json
new file mode 100644
index 0000000..ec2a443
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.expected.json
@@ -0,0 +1,61 @@
+{"type":"newline","line":2}
+{"type":"comment","line":2,"val":" foo","buffer":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"comment","line":4,"val":" bar","buffer":true}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"text","line":5,"val":"one"}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":" baz","buffer":true}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"two"}
+{"type":"outdent","line":9}
+{"type":"comment","line":9,"val":"","buffer":true}
+{"type":"start-pipeless-text","line":9}
+{"type":"text","line":10,"val":"ul"}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"  li foo"}
+{"type":"newline","line":12}
+{"type":"text","line":12,"val":""}
+{"type":"end-pipeless-text","line":12}
+{"type":"newline","line":13}
+{"type":"comment","line":13,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":13}
+{"type":"text","line":14,"val":"// inline follow"}
+{"type":"newline","line":15}
+{"type":"text","line":15,"val":"li three"}
+{"type":"newline","line":16}
+{"type":"text","line":16,"val":""}
+{"type":"end-pipeless-text","line":16}
+{"type":"newline","line":17}
+{"type":"comment","line":17,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":17}
+{"type":"text","line":18,"val":"// inline followed by tags"}
+{"type":"newline","line":19}
+{"type":"text","line":19,"val":"ul"}
+{"type":"newline","line":20}
+{"type":"text","line":20,"val":"  li four"}
+{"type":"newline","line":21}
+{"type":"text","line":21,"val":""}
+{"type":"end-pipeless-text","line":21}
+{"type":"newline","line":22}
+{"type":"comment","line":22,"val":"if IE lt 9","buffer":true}
+{"type":"start-pipeless-text","line":22}
+{"type":"text","line":23,"val":"// inline"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"script(src='/lame.js')"}
+{"type":"newline","line":25}
+{"type":"text","line":25,"val":"// end-inline"}
+{"type":"newline","line":26}
+{"type":"text","line":26,"val":""}
+{"type":"end-pipeless-text","line":26}
+{"type":"newline","line":27}
+{"type":"tag","line":27,"val":"p","selfClosing":false}
+{"type":"text","line":27,"val":"five"}
+{"type":"newline","line":29}
+{"type":"class","line":29,"val":"foo"}
+{"type":"text","line":29,"val":"// not a comment"}
+{"type":"eos","line":29}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.jade
new file mode 100644
index 0000000..4fd9aa0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.jade
@@ -0,0 +1,29 @@
+
+// foo
+ul
+  // bar
+  li one
+  // baz
+  li two
+
+//
+  ul
+    li foo
+
+// block
+  // inline follow
+  li three
+
+// block
+  // inline followed by tags
+  ul
+    li four
+
+//if IE lt 9
+  // inline
+  script(src='/lame.js')
+  // end-inline
+
+p five
+
+.foo // not a comment
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.source.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.source.expected.json
new file mode 100644
index 0000000..73610d1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.source.expected.json
@@ -0,0 +1,19 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"s/s."}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":""}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"comment","line":4,"val":" test/cases/comments.source.jade","buffer":false}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"test/cases/comments.source.jade"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"when"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"()"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.source.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.source.jade
new file mode 100644
index 0000000..934727d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/comments.source.jade
@@ -0,0 +1,9 @@
+//-
+  s/s.
+
+//- test/cases/comments.source.jade
+
+//-
+  test/cases/comments.source.jade
+  when
+  ()
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/custom-filter.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/custom-filter.expected.json
new file mode 100644
index 0000000..667d80a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/custom-filter.expected.json
@@ -0,0 +1,6 @@
+{"type":"filter","line":1,"val":"custom-filter"}
+{"type":"attrs","line":1,"attrs":[{"name":"foo","val":"'bar'","escaped":true}]}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"foo bar"}
+{"type":"end-pipeless-text","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/custom-filter.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/custom-filter.jade
new file mode 100644
index 0000000..def880a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/custom-filter.jade
@@ -0,0 +1,2 @@
+:custom-filter(foo='bar')
+  foo bar
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.custom.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.custom.expected.json
new file mode 100644
index 0000000..aacb862
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.custom.expected.json
@@ -0,0 +1,2 @@
+{"type":"doctype","line":1,"val":"custom stuff"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.custom.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.custom.jade
new file mode 100644
index 0000000..9cb2605
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.custom.jade
@@ -0,0 +1 @@
+doctype custom stuff
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.default.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.default.expected.json
new file mode 100644
index 0000000..c46ad8a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.default.expected.json
@@ -0,0 +1,11 @@
+{"type":"doctype","line":1}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"indent","line":4,"val":4}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"text","line":4,"val":"Title"}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.default.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.default.jade
new file mode 100644
index 0000000..992885e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.default.jade
@@ -0,0 +1,4 @@
+doctype
+html
+  body
+    h1 Title
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.keyword.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.keyword.expected.json
new file mode 100644
index 0000000..3c5ac9d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.keyword.expected.json
@@ -0,0 +1,2 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.keyword.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.keyword.jade
new file mode 100644
index 0000000..3dd566e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/doctype.keyword.jade
@@ -0,0 +1 @@
+doctype html
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/each.else.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/each.else.expected.json
new file mode 100644
index 0000000..f9cd4f4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/each.else.expected.json
@@ -0,0 +1,95 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var users = []","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"ul","selfClosing":false}
+{"type":"indent","line":5,"val":2}
+{"type":"each","line":5,"val":"user","code":"users"}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"code","line":6,"val":"user.name","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"code","line":7,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":8,"val":4}
+{"type":"tag","line":8,"val":"li","selfClosing":false}
+{"type":"text","line":8,"val":"no users!"}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"code","line":11,"val":"var users = [{ name: 'tobi', friends: ['loki'] }, { name: 'loki' }]","escape":false,"buffer":false}
+{"type":"newline","line":13}
+{"type":"code","line":13,"val":"if ( users)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":14,"val":2}
+{"type":"tag","line":14,"val":"ul","selfClosing":false}
+{"type":"indent","line":15,"val":4}
+{"type":"each","line":15,"val":"user","code":"users"}
+{"type":"indent","line":16,"val":6}
+{"type":"tag","line":16,"val":"li","selfClosing":false}
+{"type":"code","line":16,"val":"user.name","escape":true,"buffer":true}
+{"type":"outdent","line":17}
+{"type":"code","line":17,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":18,"val":6}
+{"type":"tag","line":18,"val":"li","selfClosing":false}
+{"type":"text","line":18,"val":"no users!"}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"code","line":20,"val":"var user = { name: 'tobi', age: 10 }","escape":false,"buffer":false}
+{"type":"newline","line":22}
+{"type":"tag","line":22,"val":"ul","selfClosing":false}
+{"type":"indent","line":23,"val":2}
+{"type":"each","line":23,"val":"val","key":"key","code":"user"}
+{"type":"indent","line":24,"val":4}
+{"type":"tag","line":24,"val":"li","selfClosing":false}
+{"type":"text","line":24,"val":"#{key}: #{val}"}
+{"type":"outdent","line":25}
+{"type":"code","line":25,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":26,"val":4}
+{"type":"tag","line":26,"val":"li","selfClosing":false}
+{"type":"text","line":26,"val":"user has no details!"}
+{"type":"outdent","line":28}
+{"type":"outdent","line":28}
+{"type":"code","line":28,"val":"var user = {}","escape":false,"buffer":false}
+{"type":"newline","line":30}
+{"type":"tag","line":30,"val":"ul","selfClosing":false}
+{"type":"indent","line":31,"val":2}
+{"type":"each","line":31,"val":"prop","key":"key","code":"user"}
+{"type":"indent","line":32,"val":4}
+{"type":"tag","line":32,"val":"li","selfClosing":false}
+{"type":"text","line":32,"val":"#{key}: #{val}"}
+{"type":"outdent","line":33}
+{"type":"code","line":33,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":34,"val":4}
+{"type":"tag","line":34,"val":"li","selfClosing":false}
+{"type":"text","line":34,"val":"user has no details!"}
+{"type":"outdent","line":36}
+{"type":"outdent","line":36}
+{"type":"tag","line":36,"val":"ul","selfClosing":false}
+{"type":"indent","line":37,"val":2}
+{"type":"each","line":37,"val":"prop","key":"key","code":"user"}
+{"type":"indent","line":38,"val":4}
+{"type":"tag","line":38,"val":"li","selfClosing":false}
+{"type":"text","line":38,"val":"#{key}: #{val}"}
+{"type":"outdent","line":39}
+{"type":"code","line":39,"val":"else","escape":false,"buffer":false}
+{"type":"indent","line":40,"val":4}
+{"type":"tag","line":40,"val":"li","selfClosing":false}
+{"type":"text","line":40,"val":"user has no details!"}
+{"type":"outdent","line":42}
+{"type":"outdent","line":42}
+{"type":"code","line":42,"val":"var user = Object.create(null)","escape":false,"buffer":false}
+{"type":"newline","line":43}
+{"type":"code","line":43,"val":"user.name = 'tobi'","escape":false,"buffer":false}
+{"type":"newline","line":45}
+{"type":"tag","line":45,"val":"ul","selfClosing":false}
+{"type":"indent","line":46,"val":2}
+{"type":"each","line":46,"val":"val","key":"key","code":"user"}
+{"type":"indent","line":47,"val":4}
+{"type":"tag","line":47,"val":"li","selfClosing":false}
+{"type":"text","line":47,"val":"#{key}: #{val}"}
+{"type":"outdent","line":48}
+{"type":"code","line":48,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":49,"val":4}
+{"type":"tag","line":49,"val":"li","selfClosing":false}
+{"type":"text","line":49,"val":"user has no details!"}
+{"type":"outdent","line":50}
+{"type":"outdent","line":50}
+{"type":"eos","line":50}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/each.else.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/each.else.jade
new file mode 100644
index 0000000..688b1ad
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/each.else.jade
@@ -0,0 +1,49 @@
+
+- var users = []
+
+ul
+  for user in users
+    li= user.name
+  else
+    li no users!
+
+
+- var users = [{ name: 'tobi', friends: ['loki'] }, { name: 'loki' }]
+
+if users
+  ul
+    for user in users
+      li= user.name
+    else
+      li no users!
+
+- var user = { name: 'tobi', age: 10 }
+
+ul
+  each val, key in user
+    li #{key}: #{val}
+  else
+    li user has no details!
+
+- var user = {}
+
+ul
+  each prop, key in user
+    li #{key}: #{val}
+  else
+    li user has no details!
+
+ul
+  - each prop, key in user
+    li #{key}: #{val}
+  - else
+    li user has no details!
+
+- var user = Object.create(null)
+- user.name = 'tobi'
+
+ul
+  each val, key in user
+    li #{key}: #{val}
+  else
+    li user has no details!
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-chars.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-chars.expected.json
new file mode 100644
index 0000000..a97de6c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-chars.expected.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"var re = /\\d+/;"}
+{"type":"end-pipeless-text","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-chars.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-chars.jade
new file mode 100644
index 0000000..f7978d6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-chars.jade
@@ -0,0 +1,2 @@
+script.
+  var re = /\d+/;
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-test.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-test.expected.json
new file mode 100644
index 0000000..9efc842
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-test.expected.json
@@ -0,0 +1,20 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"head","selfClosing":false}
+{"type":"indent","line":4,"val":8}
+{"type":"tag","line":4,"val":"title","selfClosing":false}
+{"type":"text","line":4,"val":"escape-test"}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"body","selfClosing":false}
+{"type":"indent","line":6,"val":8}
+{"type":"tag","line":6,"val":"textarea","selfClosing":false}
+{"type":"indent","line":7,"val":12}
+{"type":"code","line":7,"val":"var txt = '<param name=\"flashvars\" value=\"a=&quot;value_a&quot;&b=&quot;value_b&quot;&c=3\"/>'","escape":false,"buffer":false}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"#{txt}"}
+{"type":"outdent","line":9}
+{"type":"outdent","line":9}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-test.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-test.jade
new file mode 100644
index 0000000..168c549
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escape-test.jade
@@ -0,0 +1,8 @@
+doctype html
+html
+    head
+        title escape-test
+    body
+        textarea
+            - var txt = '<param name="flashvars" value="a=&quot;value_a&quot;&b=&quot;value_b&quot;&c=3"/>'
+            | #{txt}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escaping-class-attribute.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escaping-class-attribute.expected.json
new file mode 100644
index 0000000..c76e658
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escaping-class-attribute.expected.json
@@ -0,0 +1,19 @@
+{"type":"tag","line":1,"val":"foo","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"attr","val":"\"<%= bar %>\"","escaped":true}]}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"foo","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"class","val":"\"<%= bar %>\"","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"attr","val":"\"<%= bar %>\"","escaped":false}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"foo","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"class","val":"\"<%= bar %>\"","escaped":false}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"foo","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"class","val":"\"<%= bar %> lol rofl\"","escaped":false}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"foo","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"class","val":"\"<%= bar %> lol rofl <%= lmao %>\"","escaped":false}]}
+{"type":"newline","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escaping-class-attribute.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escaping-class-attribute.jade
new file mode 100644
index 0000000..dffbb8b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/escaping-class-attribute.jade
@@ -0,0 +1,6 @@
+foo(attr="<%= bar %>")
+foo(class="<%= bar %>")
+foo(attr!="<%= bar %>")
+foo(class!="<%= bar %>")
+foo(class!="<%= bar %> lol rofl")
+foo(class!="<%= bar %> lol rofl <%= lmao %>")
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters-empty.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters-empty.expected.json
new file mode 100644
index 0000000..cfbc039
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters-empty.expected.json
@@ -0,0 +1,14 @@
+{"type":"code","line":1,"val":"var users = [{ name: 'tobi', age: 2 }]","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"fb:users","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"each","line":4,"val":"user","code":"users"}
+{"type":"indent","line":5,"val":4}
+{"type":"tag","line":5,"val":"fb:user","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"age","val":"user.age","escaped":true}]}
+{"type":"indent","line":6,"val":6}
+{"type":"filter","line":6,"val":"cdata"}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters-empty.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters-empty.jade
new file mode 100644
index 0000000..7aa64de
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters-empty.jade
@@ -0,0 +1,6 @@
+- var users = [{ name: 'tobi', age: 2 }]
+
+fb:users
+  for user in users
+    fb:user(age=user.age)
+      :cdata
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.cdata.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.cdata.expected.json
new file mode 100644
index 0000000..fdff3fa
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.cdata.expected.json
@@ -0,0 +1,18 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"users = [{ name: 'tobi', age: 2 }]","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"fb:users","selfClosing":false}
+{"type":"indent","line":5,"val":2}
+{"type":"each","line":5,"val":"user","code":"users"}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"fb:user","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"age","val":"user.age","escaped":true}]}
+{"type":"indent","line":7,"val":6}
+{"type":"filter","line":7,"val":"cdata"}
+{"type":"start-pipeless-text","line":7}
+{"type":"text","line":8,"val":"#{user.name}"}
+{"type":"end-pipeless-text","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.cdata.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.cdata.jade
new file mode 100644
index 0000000..a9f0921
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.cdata.jade
@@ -0,0 +1,8 @@
+
+- users = [{ name: 'tobi', age: 2 }]
+
+fb:users
+  for user in users
+    fb:user(age=user.age)
+      :cdata
+        #{user.name}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.coffeescript.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.coffeescript.expected.json
new file mode 100644
index 0000000..af13e96
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.coffeescript.expected.json
@@ -0,0 +1,17 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/javascript'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"filter","line":2,"val":"coffeescript"}
+{"type":"start-pipeless-text","line":2}
+{"type":"text","line":3,"val":"regexp = /\\n/"}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"filter","line":4,"val":"coffeescript"}
+{"type":"attrs","line":4,"attrs":[{"name":"minify","val":"true","escaped":true}]}
+{"type":"start-pipeless-text","line":4}
+{"type":"text","line":5,"val":"math ="}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"  square: (value) -> value * value"}
+{"type":"end-pipeless-text","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.coffeescript.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.coffeescript.jade
new file mode 100644
index 0000000..3941b44
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.coffeescript.jade
@@ -0,0 +1,6 @@
+script(type='text/javascript')
+  :coffeescript
+    regexp = /\n/
+  :coffeescript(minify=true)
+    math =
+      square: (value) -> value * value
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.inline.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.inline.expected.json
new file mode 100644
index 0000000..3d8f639
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.inline.expected.json
@@ -0,0 +1,8 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"before "}
+{"type":"start-jade-interpolation","line":1}
+{"type":"filter","line":1,"val":"cdata"}
+{"type":"text","line":1,"val":"inside"}
+{"type":"end-jade-interpolation","line":1}
+{"type":"text","line":1,"val":" after"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.inline.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.inline.jade
new file mode 100644
index 0000000..7b57985
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.inline.jade
@@ -0,0 +1 @@
+p before #[:cdata inside] after
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.less.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.less.expected.json
new file mode 100644
index 0000000..7301cd1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.less.expected.json
@@ -0,0 +1,21 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"style","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"\"text/css\"","escaped":true}]}
+{"type":"indent","line":4,"val":6}
+{"type":"filter","line":4,"val":"less"}
+{"type":"start-pipeless-text","line":4}
+{"type":"text","line":5,"val":"@pad: 15px;"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"body {"}
+{"type":"newline","line":7}
+{"type":"text","line":7,"val":"  padding: @pad;"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"}"}
+{"type":"end-pipeless-text","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.less.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.less.jade
new file mode 100644
index 0000000..a3df945
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.less.jade
@@ -0,0 +1,8 @@
+html
+  head
+    style(type="text/css")
+      :less
+        @pad: 15px;
+        body {
+          padding: @pad;
+        }
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.markdown.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.markdown.expected.json
new file mode 100644
index 0000000..0dc68f7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.markdown.expected.json
@@ -0,0 +1,13 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"filter","line":3,"val":"markdown"}
+{"type":"start-pipeless-text","line":3}
+{"type":"text","line":4,"val":"This is _some_ awesome **markdown**"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"whoop."}
+{"type":"end-pipeless-text","line":5}
+{"type":"outdent","line":5}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.markdown.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.markdown.jade
new file mode 100644
index 0000000..a36edf8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.markdown.jade
@@ -0,0 +1,5 @@
+html
+  body
+    :markdown
+      This is _some_ awesome **markdown**
+      whoop.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.stylus.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.stylus.expected.json
new file mode 100644
index 0000000..c50bf8c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.stylus.expected.json
@@ -0,0 +1,18 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"style","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"\"text/css\"","escaped":true}]}
+{"type":"indent","line":4,"val":6}
+{"type":"filter","line":4,"val":"stylus"}
+{"type":"start-pipeless-text","line":4}
+{"type":"text","line":5,"val":"body"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"  padding: 50px"}
+{"type":"end-pipeless-text","line":6}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"body","selfClosing":false}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.stylus.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.stylus.jade
new file mode 100644
index 0000000..323d29c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/filters.stylus.jade
@@ -0,0 +1,7 @@
+html
+  head
+    style(type="text/css")
+      :stylus
+        body
+          padding: 50px
+  body
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html.expected.json
new file mode 100644
index 0000000..e844031
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html.expected.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"text-html","line":2,"val":"<ul>"}
+{"type":"indent","line":3,"val":2}
+{"type":"text-html","line":3,"val":"<li>foo</li>"}
+{"type":"newline","line":4}
+{"type":"text-html","line":4,"val":"<li>bar</li>"}
+{"type":"newline","line":5}
+{"type":"text-html","line":5,"val":"<li>baz</li>"}
+{"type":"outdent","line":6}
+{"type":"text-html","line":6,"val":"</ul>"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"You can <em>embed</em> html as well."}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html.jade
new file mode 100644
index 0000000..29787c7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html.jade
@@ -0,0 +1,8 @@
+
+<ul>
+  <li>foo</li>
+  <li>bar</li>
+  <li>baz</li>
+</ul>
+
+p You can <em>embed</em> html as well.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html5.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html5.expected.json
new file mode 100644
index 0000000..fe0aec5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html5.expected.json
@@ -0,0 +1,11 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"input","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"type","val":"'checkbox'","escaped":true},{"name":"checked","val":true,"escaped":false}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"input","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"'checkbox'","escaped":true},{"name":"checked","val":"true","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"input","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"type","val":"'checkbox'","escaped":true},{"name":"checked","val":"false","escaped":true}]}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html5.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html5.jade
new file mode 100644
index 0000000..8dc68e2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/html5.jade
@@ -0,0 +1,4 @@
+doctype html
+input(type='checkbox', checked)
+input(type='checkbox', checked=true)
+input(type='checkbox', checked=false)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-from-root.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-from-root.expected.json
new file mode 100644
index 0000000..d5d77cc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-from-root.expected.json
@@ -0,0 +1,2 @@
+{"type":"include","line":1,"val":"/auxiliary/extends-from-root"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-from-root.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-from-root.jade
new file mode 100644
index 0000000..f682dc6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-from-root.jade
@@ -0,0 +1 @@
+include /auxiliary/extends-from-root
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-of-common-template.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-of-common-template.expected.json
new file mode 100644
index 0000000..afd725e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-of-common-template.expected.json
@@ -0,0 +1,5 @@
+{"type":"include","line":1,"val":"auxiliary/extends-empty-block-1"}
+{"type":"newline","line":2}
+{"type":"include","line":2,"val":"auxiliary/extends-empty-block-2"}
+{"type":"newline","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-of-common-template.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-of-common-template.jade
new file mode 100644
index 0000000..a918085
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-extends-of-common-template.jade
@@ -0,0 +1,2 @@
+include auxiliary/extends-empty-block-1
+include auxiliary/extends-empty-block-2
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-coffee.coffee b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-coffee.coffee
new file mode 100644
index 0000000..9723cd7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-coffee.coffee
@@ -0,0 +1,2 @@
+math =
+  square: (value) -> value * value
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-stylus.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-stylus.expected.json
new file mode 100644
index 0000000..685fccd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-stylus.expected.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"style","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"\"text/css\"","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"include","line":2,"val":"some.styl","filter":"styl","attrs":null}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-stylus.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-stylus.jade
new file mode 100644
index 0000000..bf57710
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter-stylus.jade
@@ -0,0 +1,2 @@
+style(type="text/css")
+  include:styl some.styl
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter.expected.json
new file mode 100644
index 0000000..1568886
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter.expected.json
@@ -0,0 +1,17 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"some.md","filter":"md","attrs":null}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"indent","line":5,"val":6}
+{"type":"include","line":5,"val":"include-filter-coffee.coffee","filter":"coffee","attrs":{"type":"attrs","line":5,"attrs":[{"name":"minify","val":"true","escaped":true}]}}
+{"type":"outdent","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"indent","line":7,"val":6}
+{"type":"include","line":7,"val":"include-filter-coffee.coffee","filter":"coffee","attrs":{"type":"attrs","line":7,"attrs":[{"name":"minify","val":"false","escaped":true}]}}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter.jade
new file mode 100644
index 0000000..7e4edd7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-filter.jade
@@ -0,0 +1,7 @@
+html
+  body
+    include:md some.md
+    script
+      include:coffee(minify=true) include-filter-coffee.coffee
+    script
+      include:coffee(minify=false) include-filter-coffee.coffee
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text-body.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text-body.expected.json
new file mode 100644
index 0000000..0d8426f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text-body.expected.json
@@ -0,0 +1,7 @@
+{"type":"text","line":1,"val":"The message is \""}
+{"type":"newline","line":2}
+{"type":"yield","line":2}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"\""}
+{"type":"newline","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text-body.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text-body.jade
new file mode 100644
index 0000000..fdb080c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text-body.jade
@@ -0,0 +1,3 @@
+| The message is "
+yield
+| "
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text.expected.json
new file mode 100644
index 0000000..f5a07e7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text.expected.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"indent","line":4,"val":6}
+{"type":"include","line":4,"val":"include-only-text-body"}
+{"type":"indent","line":5,"val":8}
+{"type":"tag","line":5,"val":"em","selfClosing":false}
+{"type":"text","line":5,"val":"hello world"}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text.jade
new file mode 100644
index 0000000..497d419
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-only-text.jade
@@ -0,0 +1,5 @@
+html
+  body
+    p
+      include include-only-text-body
+        em hello world
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text-head.expected.json
new file mode 100644
index 0000000..19e2e4d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text-head.expected.json
@@ -0,0 +1,10 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"script","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"type","val":"'text/javascript'","escaped":true}]}
+{"type":"dot","line":2}
+{"type":"start-pipeless-text","line":2}
+{"type":"text","line":3,"val":"alert('hello world');"}
+{"type":"end-pipeless-text","line":3}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text-head.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text-head.jade
new file mode 100644
index 0000000..4e670c0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text-head.jade
@@ -0,0 +1,3 @@
+head
+  script(type='text/javascript').
+    alert('hello world');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text.expected.json
new file mode 100644
index 0000000..2bb5bd9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text.expected.json
@@ -0,0 +1,12 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"include","line":2,"val":"include-with-text-head"}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"script","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"src","val":"'/caustic.js'","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/app.js'","escaped":true}]}
+{"type":"outdent","line":5}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text.jade
new file mode 100644
index 0000000..b35c459
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include-with-text.jade
@@ -0,0 +1,4 @@
+html
+  include include-with-text-head
+    script(src='/caustic.js')
+    script(src='/app.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.script.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.script.expected.json
new file mode 100644
index 0000000..2355940
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.script.expected.json
@@ -0,0 +1,7 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"id","line":1,"val":"pet-template"}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"include","line":2,"val":"auxiliary/pet"}
+{"type":"outdent","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.script.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.script.jade
new file mode 100644
index 0000000..c8a6c19
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.script.jade
@@ -0,0 +1,2 @@
+script#pet-template(type='text/x-template')
+  include auxiliary/pet
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.yield.nested.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.yield.nested.expected.json
new file mode 100644
index 0000000..efaf5ac
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.yield.nested.expected.json
@@ -0,0 +1,10 @@
+{"type":"newline","line":2}
+{"type":"include","line":2,"val":"auxiliary/yield-nested"}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"text","line":3,"val":"some content"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"and some more"}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.yield.nested.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.yield.nested.jade
new file mode 100644
index 0000000..b8d4ba2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/include.yield.nested.jade
@@ -0,0 +1,4 @@
+
+include auxiliary/yield-nested
+  p some content
+  p and some more
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes-with-ext-js.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes-with-ext-js.expected.json
new file mode 100644
index 0000000..239ec2c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes-with-ext-js.expected.json
@@ -0,0 +1,8 @@
+{"type":"tag","line":1,"val":"pre","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"code","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"javascript-new-lines.js"}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes-with-ext-js.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes-with-ext-js.jade
new file mode 100644
index 0000000..65bfa8a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes-with-ext-js.jade
@@ -0,0 +1,3 @@
+pre
+  code
+    include javascript-new-lines.js
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes.expected.json
new file mode 100644
index 0000000..6857e59
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes.expected.json
@@ -0,0 +1,18 @@
+{"type":"newline","line":2}
+{"type":"include","line":2,"val":"auxiliary/mixins"}
+{"type":"newline","line":4}
+{"type":"call","line":4,"val":"foo"}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"body","selfClosing":false}
+{"type":"indent","line":7,"val":2}
+{"type":"include","line":7,"val":"auxiliary/smile.html"}
+{"type":"newline","line":8}
+{"type":"include","line":8,"val":"auxiliary/escapes.html"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"script","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"type","val":"\"text/javascript\"","escaped":true}]}
+{"type":"indent","line":10,"val":4}
+{"type":"include","line":10,"val":"auxiliary/includable.js","filter":"js","attrs":null}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"eos","line":11}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes.jade
new file mode 100644
index 0000000..df2592e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/includes.jade
@@ -0,0 +1,10 @@
+
+include auxiliary/mixins
+
++foo
+
+body
+  include auxiliary/smile.html
+  include auxiliary/escapes.html
+  script(type="text/javascript")
+    include:js auxiliary/includable.js
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.alert-dialog.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.alert-dialog.expected.json
new file mode 100644
index 0000000..6ae9d78
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.alert-dialog.expected.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/dialog"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"content","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"h1","selfClosing":false}
+{"type":"text","line":5,"val":"Alert!"}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"text","line":6,"val":"I'm an alert!"}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.alert-dialog.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.alert-dialog.jade
new file mode 100644
index 0000000..0d02d76
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.alert-dialog.jade
@@ -0,0 +1,6 @@
+
+extends auxiliary/dialog
+
+block content
+  h1 Alert!
+  p I'm an alert!
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.defaults.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.defaults.expected.json
new file mode 100644
index 0000000..dc94bf2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.defaults.expected.json
@@ -0,0 +1,18 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"block","line":3,"val":"head","mode":"replace"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'keymaster.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'caustic.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.defaults.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.defaults.jade
new file mode 100644
index 0000000..aaead83
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.defaults.jade
@@ -0,0 +1,6 @@
+html
+  head
+    block head
+      script(src='jquery.js')
+      script(src='keymaster.js')
+      script(src='caustic.js')
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.expected.json
new file mode 100644
index 0000000..f2f3354
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.expected.json
@@ -0,0 +1,17 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"block","line":7,"val":"content","mode":"replace"}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"h2","selfClosing":false}
+{"type":"text","line":8,"val":"Page"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"Some content"}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.expected.json
new file mode 100644
index 0000000..f2f3354
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.expected.json
@@ -0,0 +1,17 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"block","line":7,"val":"content","mode":"replace"}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"h2","selfClosing":false}
+{"type":"text","line":8,"val":"Page"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"Some content"}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.include.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.include.expected.json
new file mode 100644
index 0000000..e65f218
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.include.expected.json
@@ -0,0 +1,25 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout.include"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"block","line":7,"val":"content","mode":"replace"}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"h2","selfClosing":false}
+{"type":"text","line":8,"val":"Page"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"Some content"}
+{"type":"outdent","line":11}
+{"type":"block","line":11,"val":"window-content","mode":"replace"}
+{"type":"indent","line":12,"val":2}
+{"type":"tag","line":12,"val":"h2","selfClosing":false}
+{"type":"text","line":12,"val":"Awesome"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"text","line":13,"val":"Now we can extend included blocks!"}
+{"type":"outdent","line":13}
+{"type":"eos","line":13}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.include.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.include.jade
new file mode 100644
index 0000000..efa29f0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.include.jade
@@ -0,0 +1,13 @@
+
+extend auxiliary/layout.include
+
+block head
+  script(src='jquery.js')
+
+block content
+  h2 Page
+  p Some content
+
+block window-content
+  h2 Awesome
+  p Now we can extend included blocks!
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.jade
new file mode 100644
index 0000000..1fabb55
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.jade
@@ -0,0 +1,9 @@
+
+extend auxiliary/layout
+
+block head
+  script(src='jquery.js')
+
+block content
+  h2 Page
+  p Some content
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.block.expected.json
new file mode 100644
index 0000000..7405423
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.block.expected.json
@@ -0,0 +1,8 @@
+{"type":"extends","line":1,"val":"auxiliary/inheritance.extend.mixin.block"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"content","mode":"replace"}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"Hello World!"}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.block.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.block.jade
new file mode 100644
index 0000000..398627c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.block.jade
@@ -0,0 +1,4 @@
+extend auxiliary/inheritance.extend.mixin.block
+
+block content
+  p Hello World!
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.expected.json
new file mode 100644
index 0000000..eaea287
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.expected.json
@@ -0,0 +1,21 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"mixin","line":4,"val":"article","args":"title"}
+{"type":"indent","line":5,"val":2}
+{"type":"code","line":5,"val":"if ( title)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"h1","selfClosing":false}
+{"type":"code","line":6,"val":"title","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"mixin-block","line":7}
+{"type":"outdent","line":9}
+{"type":"block","line":9,"val":"content","mode":"replace"}
+{"type":"indent","line":10,"val":2}
+{"type":"call","line":10,"val":"article","args":"\"The meaning of life\""}
+{"type":"indent","line":11,"val":4}
+{"type":"tag","line":11,"val":"p","selfClosing":false}
+{"type":"text","line":11,"val":"Foo bar baz!"}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"eos","line":11}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.jade
new file mode 100644
index 0000000..e369e76
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.mixins.jade
@@ -0,0 +1,11 @@
+
+extend auxiliary/layout
+
+mixin article(title)
+  if title
+    h1= title
+  block
+
+block content
+  +article("The meaning of life")
+    p Foo bar baz!
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.recursive.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.recursive.expected.json
new file mode 100644
index 0000000..0033858
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.recursive.expected.json
@@ -0,0 +1,8 @@
+{"type":"extends","line":1,"val":"/auxiliary/inheritance.extend.recursive-parent.jade"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"parent","mode":"replace"}
+{"type":"indent","line":4,"val":4}
+{"type":"tag","line":4,"val":"h4","selfClosing":false}
+{"type":"text","line":4,"val":"child"}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.recursive.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.recursive.jade
new file mode 100644
index 0000000..2b14e21
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.recursive.jade
@@ -0,0 +1,4 @@
+extends /auxiliary/inheritance.extend.recursive-parent.jade
+
+block parent
+    h4 child
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.whitespace.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.whitespace.expected.json
new file mode 100644
index 0000000..753128a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.whitespace.expected.json
@@ -0,0 +1,17 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":6,"val":2}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":8}
+{"type":"block","line":8,"val":"content","mode":"replace"}
+{"type":"indent","line":12,"val":2}
+{"type":"tag","line":12,"val":"h2","selfClosing":false}
+{"type":"text","line":12,"val":"Page"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"text","line":13,"val":"Some content"}
+{"type":"outdent","line":13}
+{"type":"eos","line":13}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.whitespace.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.whitespace.jade
new file mode 100644
index 0000000..ebdb8e4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.extend.whitespace.jade
@@ -0,0 +1,13 @@
+
+extend auxiliary/layout
+
+block head
+
+  script(src='jquery.js')
+
+block content
+  
+    
+  
+  h2 Page
+  p Some content
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.jade
new file mode 100644
index 0000000..567408f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inheritance.jade
@@ -0,0 +1,9 @@
+
+extends auxiliary/layout
+
+block head
+  script(src='jquery.js')
+
+block content
+  h2 Page
+  p Some content
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inline-tag.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inline-tag.expected.json
new file mode 100644
index 0000000..8e6ba6a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inline-tag.expected.json
@@ -0,0 +1,51 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"bing "}
+{"type":"start-jade-interpolation","line":1}
+{"type":"tag","line":1,"val":"strong","selfClosing":false}
+{"type":"text","line":1,"val":"foo"}
+{"type":"end-jade-interpolation","line":1}
+{"type":"text","line":1,"val":" bong"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"dot","line":3}
+{"type":"start-pipeless-text","line":3}
+{"type":"text","line":4,"val":"bing"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"start-jade-interpolation","line":5}
+{"type":"tag","line":5,"val":"strong","selfClosing":false}
+{"type":"text","line":5,"val":"foo"}
+{"type":"end-jade-interpolation","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"bong"}
+{"type":"newline","line":7}
+{"type":"text","line":7,"val":""}
+{"type":"end-pipeless-text","line":7}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"indent","line":9,"val":2}
+{"type":"text","line":9,"val":"bing"}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":""}
+{"type":"start-jade-interpolation","line":10}
+{"type":"tag","line":10,"val":"strong","selfClosing":false}
+{"type":"text","line":10,"val":"foo"}
+{"type":"end-jade-interpolation","line":10}
+{"type":"text","line":10,"val":""}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"bong"}
+{"type":"outdent","line":13}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"dot","line":13}
+{"type":"start-pipeless-text","line":13}
+{"type":"text","line":14,"val":"#[strong escaped]"}
+{"type":"newline","line":15}
+{"type":"text","line":15,"val":"#["}
+{"type":"start-jade-interpolation","line":15}
+{"type":"tag","line":15,"val":"strong","selfClosing":false}
+{"type":"text","line":15,"val":"escaped"}
+{"type":"end-jade-interpolation","line":15}
+{"type":"text","line":15,"val":""}
+{"type":"end-pipeless-text","line":15}
+{"type":"eos","line":15}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inline-tag.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inline-tag.jade
new file mode 100644
index 0000000..9712123
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/inline-tag.jade
@@ -0,0 +1,15 @@
+p bing #[strong foo] bong
+
+p.
+  bing
+  #[strong foo]
+  bong
+
+p
+  | bing
+  | #[strong foo]
+  | bong
+
+p.
+  \#[strong escaped]
+  \#[#[strong escaped]
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/intepolated-elements.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/intepolated-elements.expected.json
new file mode 100644
index 0000000..359c76e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/intepolated-elements.expected.json
@@ -0,0 +1,29 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":""}
+{"type":"start-jade-interpolation","line":1}
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"class","line":1,"val":"rho"}
+{"type":"attrs","line":1,"attrs":[{"name":"href","val":"'#'","escaped":true},{"name":"class","val":"'rho--modifier'","escaped":true}]}
+{"type":"text","line":1,"val":"with inline link"}
+{"type":"end-jade-interpolation","line":1}
+{"type":"text","line":1,"val":""}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"text","line":2,"val":"Some text "}
+{"type":"start-jade-interpolation","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"class","line":2,"val":"rho"}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'#'","escaped":true},{"name":"class","val":"'rho--modifier'","escaped":true}]}
+{"type":"end-jade-interpolation","line":2}
+{"type":"text","line":2,"val":""}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"text","line":3,"val":"Some text "}
+{"type":"start-jade-interpolation","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"class","line":3,"val":"rho"}
+{"type":"attrs","line":3,"attrs":[{"name":"href","val":"'#'","escaped":true},{"name":"class","val":"'rho--modifier'","escaped":true}]}
+{"type":"text","line":3,"val":"with inline link"}
+{"type":"end-jade-interpolation","line":3}
+{"type":"text","line":3,"val":""}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/intepolated-elements.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/intepolated-elements.jade
new file mode 100644
index 0000000..5fe8bcf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/intepolated-elements.jade
@@ -0,0 +1,3 @@
+p #[a.rho(href='#', class='rho--modifier') with inline link]
+p Some text #[a.rho(href='#', class='rho--modifier')]
+p Some text #[a.rho(href='#', class='rho--modifier') with inline link]
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/interpolation.escape.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/interpolation.escape.expected.json
new file mode 100644
index 0000000..d343c12
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/interpolation.escape.expected.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var id = 42;","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"text","line":4,"val":"some"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"\\#{text}"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"here"}
+{"type":"newline","line":7}
+{"type":"text","line":7,"val":"My ID #{\"is {\" + id + \"}\"}"}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/interpolation.escape.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/interpolation.escape.jade
new file mode 100644
index 0000000..cff251b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/interpolation.escape.jade
@@ -0,0 +1,7 @@
+
+- var id = 42;
+foo
+  | some
+  | \#{text}
+  | here
+  | My ID #{"is {" + id + "}"}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/javascript-new-lines.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/javascript-new-lines.js
new file mode 100644
index 0000000..6893341
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/javascript-new-lines.js
@@ -0,0 +1 @@
+var x = "\n here is some \n new lined text";
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.expected.json
new file mode 100644
index 0000000..eaaea3e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.expected.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/append/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"append"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.jade
new file mode 100644
index 0000000..ec2a21f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.jade
@@ -0,0 +1,6 @@
+
+extends ../fixtures/append/app-layout
+
+block append head
+  script(src='foo.js')
+  script(src='bar.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.without-block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.without-block.expected.json
new file mode 100644
index 0000000..1857b30
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.without-block.expected.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/append-without-block/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"append"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.without-block.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.without-block.jade
new file mode 100644
index 0000000..ce0c56d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.append.without-block.jade
@@ -0,0 +1,6 @@
+
+extends ../fixtures/append-without-block/app-layout
+
+append head
+  script(src='foo.js')
+  script(src='bar.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.multi.append.prepend.block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.multi.append.prepend.block.expected.json
new file mode 100644
index 0000000..10649e3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.multi.append.prepend.block.expected.json
@@ -0,0 +1,41 @@
+{"type":"extends","line":1,"val":"../fixtures/multi-append-prepend-block/redefine"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"content","mode":"append"}
+{"type":"indent","line":4,"val":1}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"class","line":4,"val":"first"}
+{"type":"class","line":4,"val":"append"}
+{"type":"text","line":4,"val":"Something appended to content"}
+{"type":"outdent","line":6}
+{"type":"block","line":6,"val":"content","mode":"prepend"}
+{"type":"indent","line":7,"val":1}
+{"type":"tag","line":7,"val":"p","selfClosing":false}
+{"type":"class","line":7,"val":"first"}
+{"type":"class","line":7,"val":"prepend"}
+{"type":"text","line":7,"val":"Something prepended to content"}
+{"type":"outdent","line":9}
+{"type":"block","line":9,"val":"content","mode":"append"}
+{"type":"indent","line":10,"val":1}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"class","line":10,"val":"last"}
+{"type":"class","line":10,"val":"append"}
+{"type":"text","line":10,"val":"Last append must be most last"}
+{"type":"outdent","line":12}
+{"type":"block","line":12,"val":"content","mode":"prepend"}
+{"type":"indent","line":13,"val":1}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"class","line":13,"val":"first"}
+{"type":"class","line":13,"val":"prepend"}
+{"type":"text","line":13,"val":"Last prepend must appear at top"}
+{"type":"outdent","line":15}
+{"type":"block","line":15,"val":"head","mode":"append"}
+{"type":"indent","line":16,"val":1}
+{"type":"tag","line":16,"val":"script","selfClosing":false}
+{"type":"attrs","line":16,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":18}
+{"type":"block","line":18,"val":"head","mode":"prepend"}
+{"type":"indent","line":19,"val":1}
+{"type":"tag","line":19,"val":"script","selfClosing":false}
+{"type":"attrs","line":19,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"outdent","line":20}
+{"type":"eos","line":20}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.multi.append.prepend.block.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.multi.append.prepend.block.jade
new file mode 100644
index 0000000..05f3960
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.multi.append.prepend.block.jade
@@ -0,0 +1,19 @@
+extends ../fixtures/multi-append-prepend-block/redefine
+
+append content
+	p.first.append Something appended to content
+
+prepend content
+	p.first.prepend Something prepended to content
+
+append content
+	p.last.append Last append must be most last
+
+prepend content
+	p.first.prepend Last prepend must appear at top
+
+append head
+	script(src='jquery.js')
+
+prepend head
+	script(src='foo.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.expected.json
new file mode 100644
index 0000000..721e04c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.expected.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/prepend/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"prepend"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.jade
new file mode 100644
index 0000000..f593c09
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.jade
@@ -0,0 +1,6 @@
+
+extends ../fixtures/prepend/app-layout
+
+block prepend head
+  script(src='foo.js')
+  script(src='bar.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.without-block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.without-block.expected.json
new file mode 100644
index 0000000..f39e00b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.without-block.expected.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/prepend-without-block/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"prepend"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.without-block.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.without-block.jade
new file mode 100644
index 0000000..79310da
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/layout.prepend.without-block.jade
@@ -0,0 +1,6 @@
+
+extends ../fixtures/prepend-without-block/app-layout
+
+prepend head
+  script(src='foo.js')
+  script(src='bar.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-at-end-of-file.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-at-end-of-file.expected.json
new file mode 100644
index 0000000..9b5ff54
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-at-end-of-file.expected.json
@@ -0,0 +1,8 @@
+{"type":"include","line":1,"val":"./auxiliary/mixin-at-end-of-file.jade"}
+{"type":"newline","line":3}
+{"type":"call","line":3,"val":"slide","args":""}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"some awesome content"}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-at-end-of-file.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-at-end-of-file.jade
new file mode 100644
index 0000000..c78c442
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-at-end-of-file.jade
@@ -0,0 +1,4 @@
+include ./auxiliary/mixin-at-end-of-file.jade
+
++slide()
+  p some awesome content
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-block-with-space.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-block-with-space.expected.json
new file mode 100644
index 0000000..2727e16
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-block-with-space.expected.json
@@ -0,0 +1,12 @@
+{"type":"mixin","line":1,"val":"m","args":"id"}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"div","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"mixin-block","line":3}
+{"type":"outdent","line":5}
+{"type":"outdent","line":5}
+{"type":"call","line":5,"val":"m","args":""}
+{"type":"indent","line":6,"val":2}
+{"type":"text","line":6,"val":"This text should appear"}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-block-with-space.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-block-with-space.jade
new file mode 100644
index 0000000..471aac8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-block-with-space.jade
@@ -0,0 +1,6 @@
+mixin m(id)
+  div
+    block    
+
++m()
+  | This text should appear
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-hoist.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-hoist.expected.json
new file mode 100644
index 0000000..fd0ea98
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-hoist.expected.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"mixin","line":2,"val":"foo","args":null}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"code","line":3,"val":"title","escape":true,"buffer":true}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"html","selfClosing":false}
+{"type":"indent","line":6,"val":2}
+{"type":"tag","line":6,"val":"body","selfClosing":false}
+{"type":"indent","line":7,"val":4}
+{"type":"mixin","line":7,"val":"foo","args":null}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-hoist.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-hoist.jade
new file mode 100644
index 0000000..64601b5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-hoist.jade
@@ -0,0 +1,7 @@
+
+mixin foo()
+  h1= title
+
+html
+  body
+    mixin foo
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-via-include.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-via-include.expected.json
new file mode 100644
index 0000000..439b3e4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-via-include.expected.json
@@ -0,0 +1,7 @@
+{"type":"comment","line":1,"val":" regression test for https://github.com/jadejs/jade/issues/1435","buffer":false}
+{"type":"text","line":2,"val":""}
+{"type":"newline","line":3}
+{"type":"include","line":3,"val":"../fixtures/mixin-include.jade"}
+{"type":"newline","line":5}
+{"type":"call","line":5,"val":"bang"}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-via-include.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-via-include.jade
new file mode 100644
index 0000000..a7bd0cd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin-via-include.jade
@@ -0,0 +1,5 @@
+//- regression test for https://github.com/jadejs/jade/issues/1435
+
+include ../fixtures/mixin-include.jade
+
++bang
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.attrs.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.attrs.expected.json
new file mode 100644
index 0000000..285c48a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.attrs.expected.json
@@ -0,0 +1,113 @@
+{"type":"mixin","line":1,"val":"centered","args":"title"}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"div","selfClosing":false}
+{"type":"class","line":2,"val":"centered"}
+{"type":"attrs","line":2,"attrs":[{"name":"id","val":"attributes.id","escaped":true}]}
+{"type":"indent","line":3,"val":4}
+{"type":"code","line":3,"val":"if (title)","escape":false,"buffer":false}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"class","val":"attributes.class","escaped":true}]}
+{"type":"code","line":4,"val":"title","escape":true,"buffer":true}
+{"type":"outdent","line":5}
+{"type":"mixin-block","line":5}
+{"type":"newline","line":6}
+{"type":"code","line":6,"val":"if (attributes.href)","escape":false,"buffer":false}
+{"type":"indent","line":7,"val":6}
+{"type":"class","line":7,"val":"footer"}
+{"type":"indent","line":8,"val":8}
+{"type":"tag","line":8,"val":"a","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"href","val":"attributes.href","escaped":true}]}
+{"type":"text","line":8,"val":"Back"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"mixin","line":10,"val":"main","args":"title"}
+{"type":"indent","line":11,"val":2}
+{"type":"tag","line":11,"val":"div","selfClosing":false}
+{"type":"class","line":11,"val":"stretch"}
+{"type":"indent","line":12,"val":4}
+{"type":"call","line":12,"val":"centered","args":"title"}
+{"type":"class","line":12,"val":"highlight"}
+{"type":"&attributes","line":12,"val":"attributes"}
+{"type":"indent","line":13,"val":6}
+{"type":"mixin-block","line":13}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"mixin","line":15,"val":"bottom","args":null}
+{"type":"indent","line":16,"val":2}
+{"type":"tag","line":16,"val":"div","selfClosing":false}
+{"type":"class","line":16,"val":"bottom"}
+{"type":"&attributes","line":16,"val":"attributes"}
+{"type":"indent","line":17,"val":4}
+{"type":"mixin-block","line":17}
+{"type":"outdent","line":19}
+{"type":"outdent","line":19}
+{"type":"tag","line":19,"val":"body","selfClosing":false}
+{"type":"indent","line":20,"val":2}
+{"type":"call","line":20,"val":"centered"}
+{"type":"id","line":20,"val":"First"}
+{"type":"text","line":20,"val":"Hello World"}
+{"type":"newline","line":21}
+{"type":"call","line":21,"val":"centered","args":"'Section 1'"}
+{"type":"id","line":21,"val":"Second"}
+{"type":"indent","line":22,"val":4}
+{"type":"tag","line":22,"val":"p","selfClosing":false}
+{"type":"text","line":22,"val":"Some important content."}
+{"type":"outdent","line":23}
+{"type":"call","line":23,"val":"centered","args":"'Section 2'"}
+{"type":"id","line":23,"val":"Third"}
+{"type":"class","line":23,"val":"foo"}
+{"type":"attrs","line":23,"attrs":[{"name":"href","val":"'menu.html'","escaped":true},{"name":"class","val":"'bar'","escaped":true}]}
+{"type":"indent","line":24,"val":4}
+{"type":"tag","line":24,"val":"p","selfClosing":false}
+{"type":"text","line":24,"val":"Even more important content."}
+{"type":"outdent","line":25}
+{"type":"call","line":25,"val":"main","args":"'Section 3'"}
+{"type":"attrs","line":25,"attrs":[{"name":"href","val":"'#'","escaped":true}]}
+{"type":"indent","line":26,"val":4}
+{"type":"tag","line":26,"val":"p","selfClosing":false}
+{"type":"text","line":26,"val":"Last content."}
+{"type":"outdent","line":27}
+{"type":"call","line":27,"val":"bottom"}
+{"type":"class","line":27,"val":"foo"}
+{"type":"attrs","line":27,"attrs":[{"name":"class","val":"'bar'","escaped":true},{"name":"name","val":"'end'","escaped":true},{"name":"id","val":"'Last'","escaped":true},{"name":"data-attr","val":"'baz'","escaped":true}]}
+{"type":"indent","line":28,"val":4}
+{"type":"tag","line":28,"val":"p","selfClosing":false}
+{"type":"text","line":28,"val":"Some final words."}
+{"type":"outdent","line":29}
+{"type":"call","line":29,"val":"bottom"}
+{"type":"attrs","line":29,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"outdent","line":31}
+{"type":"mixin","line":31,"val":"foo","args":null}
+{"type":"indent","line":32,"val":2}
+{"type":"tag","line":32,"val":"div","selfClosing":false}
+{"type":"class","line":32,"val":"thing"}
+{"type":"attrs","line":32,"attrs":[{"name":"attr1","val":"'foo'","escaped":true},{"name":"attr2","val":"'bar'","escaped":true}]}
+{"type":"&attributes","line":32,"val":"attributes"}
+{"type":"outdent","line":34}
+{"type":"code","line":34,"val":"var val = '<biz>'","escape":false,"buffer":false}
+{"type":"newline","line":35}
+{"type":"code","line":35,"val":"var classes = ['foo', 'bar']","escape":false,"buffer":false}
+{"type":"newline","line":36}
+{"type":"call","line":36,"val":"foo"}
+{"type":"attrs","line":36,"attrs":[{"name":"attr3","val":"'baz'","escaped":true},{"name":"data-foo","val":"val","escaped":true},{"name":"data-bar","val":"val","escaped":false},{"name":"class","val":"classes","escaped":true}]}
+{"type":"class","line":36,"val":"thunk"}
+{"type":"newline","line":38}
+{"type":"comment","line":38,"val":" Regression test for #1424","buffer":false}
+{"type":"newline","line":39}
+{"type":"mixin","line":39,"val":"work_filmstrip_item","args":"work"}
+{"type":"indent","line":40,"val":2}
+{"type":"tag","line":40,"val":"div","selfClosing":false}
+{"type":"&attributes","line":40,"val":"attributes"}
+{"type":"code","line":40,"val":"work","escape":true,"buffer":true}
+{"type":"outdent","line":41}
+{"type":"call","line":41,"val":"work_filmstrip_item","args":"'work'"}
+{"type":"attrs","line":41,"attrs":[{"name":"data-profile","val":"'profile'","escaped":true},{"name":"data-creator-name","val":"'name'","escaped":true}]}
+{"type":"newline","line":43}
+{"type":"call","line":43,"val":"foo"}
+{"type":"attrs","line":43,"attrs":[{"name":"attr3","val":"\"qux\"","escaped":true},{"name":"class","val":"\"baz\"","escaped":true}]}
+{"type":"newline","line":47}
+{"type":"eos","line":47}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.attrs.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.attrs.jade
new file mode 100644
index 0000000..f2e1dc9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.attrs.jade
@@ -0,0 +1,46 @@
+mixin centered(title)
+  div.centered(id=attributes.id)
+    - if (title)
+      h1(class=attributes.class)= title
+    block
+    - if (attributes.href)
+      .footer
+        a(href=attributes.href) Back
+
+mixin main(title)
+  div.stretch
+    +centered(title).highlight&attributes(attributes)
+      block
+
+mixin bottom
+  div.bottom&attributes(attributes)
+    block
+
+body
+  +centered#First Hello World
+  +centered('Section 1')#Second
+    p Some important content.
+  +centered('Section 2')#Third.foo(href='menu.html', class='bar')
+    p Even more important content.
+  +main('Section 3')(href='#')
+    p Last content.
+  +bottom.foo(class='bar', name='end', id='Last', data-attr='baz')
+    p Some final words.
+  +bottom(class=['class1', 'class2'])
+
+mixin foo
+  div.thing(attr1='foo', attr2='bar')&attributes(attributes)
+
+- var val = '<biz>'
+- var classes = ['foo', 'bar']
++foo(attr3='baz' data-foo=val data-bar!=val class=classes).thunk
+
+//- Regression test for #1424
+mixin work_filmstrip_item(work)
+  div&attributes(attributes)= work
++work_filmstrip_item('work')("data-profile"='profile', "data-creator-name"='name')
+
++foo(
+  attr3="qux"
+  class="baz"
+)
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.block-tag-behaviour.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.block-tag-behaviour.expected.json
new file mode 100644
index 0000000..be7a65b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.block-tag-behaviour.expected.json
@@ -0,0 +1,56 @@
+{"type":"newline","line":2}
+{"type":"mixin","line":2,"val":"article","args":"name"}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"section","selfClosing":false}
+{"type":"class","line":3,"val":"article"}
+{"type":"indent","line":4,"val":4}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"code","line":4,"val":"name","escape":true,"buffer":true}
+{"type":"newline","line":5}
+{"type":"mixin-block","line":5}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"html","selfClosing":false}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"body","selfClosing":false}
+{"type":"indent","line":9,"val":4}
+{"type":"call","line":9,"val":"article","args":"'Foo'"}
+{"type":":","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"I'm article foo"}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"mixin","line":11,"val":"article","args":"name"}
+{"type":"indent","line":12,"val":2}
+{"type":"tag","line":12,"val":"section","selfClosing":false}
+{"type":"class","line":12,"val":"article"}
+{"type":"indent","line":13,"val":4}
+{"type":"tag","line":13,"val":"h1","selfClosing":false}
+{"type":"code","line":13,"val":"name","escape":true,"buffer":true}
+{"type":"newline","line":14}
+{"type":"tag","line":14,"val":"p","selfClosing":false}
+{"type":"indent","line":15,"val":6}
+{"type":"mixin-block","line":15}
+{"type":"outdent","line":17}
+{"type":"outdent","line":17}
+{"type":"outdent","line":17}
+{"type":"tag","line":17,"val":"html","selfClosing":false}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"body","selfClosing":false}
+{"type":"indent","line":19,"val":4}
+{"type":"call","line":19,"val":"article","args":"'Something'"}
+{"type":"dot","line":19}
+{"type":"start-pipeless-text","line":19}
+{"type":"text","line":20,"val":"I'm a much longer"}
+{"type":"newline","line":21}
+{"type":"text","line":21,"val":"text-only article,"}
+{"type":"newline","line":22}
+{"type":"text","line":22,"val":"but you can still"}
+{"type":"newline","line":23}
+{"type":"text","line":23,"val":"inline html tags"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"in me if you want."}
+{"type":"end-pipeless-text","line":24}
+{"type":"outdent","line":24}
+{"type":"outdent","line":24}
+{"type":"eos","line":24}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.block-tag-behaviour.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.block-tag-behaviour.jade
new file mode 100644
index 0000000..1d2d2d3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.block-tag-behaviour.jade
@@ -0,0 +1,24 @@
+
+mixin article(name)
+  section.article
+    h1= name
+    block
+
+html
+  body
+    +article('Foo'): p I'm article foo
+
+mixin article(name)
+  section.article
+    h1= name
+    p
+      block
+
+html
+  body
+    +article('Something').
+      I'm a much longer
+      text-only article,
+      but you can still
+      inline html tags
+      in me if you want.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.blocks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.blocks.expected.json
new file mode 100644
index 0000000..e336f46
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.blocks.expected.json
@@ -0,0 +1,87 @@
+{"type":"newline","line":3}
+{"type":"mixin","line":3,"val":"form","args":"method, action"}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"form","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"method","val":"method","escaped":true},{"name":"action","val":"action","escaped":true}]}
+{"type":"indent","line":5,"val":4}
+{"type":"code","line":5,"val":"var csrf_token_from_somewhere = 'hey'","escape":false,"buffer":false}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"input","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'hidden'","escaped":true},{"name":"name","val":"'_csrf'","escaped":true},{"name":"value","val":"csrf_token_from_somewhere","escaped":true}]}
+{"type":"newline","line":7}
+{"type":"mixin-block","line":7}
+{"type":"outdent","line":9}
+{"type":"outdent","line":9}
+{"type":"tag","line":9,"val":"html","selfClosing":false}
+{"type":"indent","line":10,"val":2}
+{"type":"tag","line":10,"val":"body","selfClosing":false}
+{"type":"indent","line":11,"val":4}
+{"type":"call","line":11,"val":"form","args":"'GET', '/search'"}
+{"type":"indent","line":12,"val":6}
+{"type":"tag","line":12,"val":"input","selfClosing":false}
+{"type":"attrs","line":12,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'query'","escaped":true},{"name":"placeholder","val":"'Search'","escaped":true}]}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"input","selfClosing":false}
+{"type":"attrs","line":13,"attrs":[{"name":"type","val":"'submit'","escaped":true},{"name":"value","val":"'Search'","escaped":true}]}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"tag","line":15,"val":"html","selfClosing":false}
+{"type":"indent","line":16,"val":2}
+{"type":"tag","line":16,"val":"body","selfClosing":false}
+{"type":"indent","line":17,"val":4}
+{"type":"call","line":17,"val":"form","args":"'POST', '/search'"}
+{"type":"indent","line":18,"val":6}
+{"type":"tag","line":18,"val":"input","selfClosing":false}
+{"type":"attrs","line":18,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'query'","escaped":true},{"name":"placeholder","val":"'Search'","escaped":true}]}
+{"type":"newline","line":19}
+{"type":"tag","line":19,"val":"input","selfClosing":false}
+{"type":"attrs","line":19,"attrs":[{"name":"type","val":"'submit'","escaped":true},{"name":"value","val":"'Search'","escaped":true}]}
+{"type":"outdent","line":21}
+{"type":"outdent","line":21}
+{"type":"outdent","line":21}
+{"type":"tag","line":21,"val":"html","selfClosing":false}
+{"type":"indent","line":22,"val":2}
+{"type":"tag","line":22,"val":"body","selfClosing":false}
+{"type":"indent","line":23,"val":4}
+{"type":"call","line":23,"val":"form","args":"'POST', '/search'"}
+{"type":"outdent","line":25}
+{"type":"outdent","line":25}
+{"type":"mixin","line":25,"val":"bar","args":null}
+{"type":"indent","line":26,"val":2}
+{"type":"id","line":26,"val":"bar"}
+{"type":"indent","line":27,"val":4}
+{"type":"mixin-block","line":27}
+{"type":"outdent","line":29}
+{"type":"outdent","line":29}
+{"type":"mixin","line":29,"val":"foo","args":null}
+{"type":"indent","line":30,"val":2}
+{"type":"id","line":30,"val":"foo"}
+{"type":"indent","line":31,"val":4}
+{"type":"call","line":31,"val":"bar"}
+{"type":"indent","line":32,"val":6}
+{"type":"mixin-block","line":32}
+{"type":"outdent","line":34}
+{"type":"outdent","line":34}
+{"type":"outdent","line":34}
+{"type":"call","line":34,"val":"foo"}
+{"type":"indent","line":35,"val":2}
+{"type":"tag","line":35,"val":"p","selfClosing":false}
+{"type":"text","line":35,"val":"one"}
+{"type":"newline","line":36}
+{"type":"tag","line":36,"val":"p","selfClosing":false}
+{"type":"text","line":36,"val":"two"}
+{"type":"newline","line":37}
+{"type":"tag","line":37,"val":"p","selfClosing":false}
+{"type":"text","line":37,"val":"three"}
+{"type":"outdent","line":40}
+{"type":"mixin","line":40,"val":"baz","args":null}
+{"type":"indent","line":41,"val":4}
+{"type":"id","line":41,"val":"baz"}
+{"type":"indent","line":42,"val":8}
+{"type":"mixin-block","line":42}
+{"type":"outdent","line":44}
+{"type":"outdent","line":44}
+{"type":"call","line":44,"val":"baz","args":""}
+{"type":"code","line":44,"val":"'123'","escape":true,"buffer":true}
+{"type":"eos","line":44}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.blocks.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.blocks.jade
new file mode 100644
index 0000000..30c9990
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.blocks.jade
@@ -0,0 +1,44 @@
+
+
+mixin form(method, action)
+  form(method=method, action=action)
+    - var csrf_token_from_somewhere = 'hey'
+    input(type='hidden', name='_csrf', value=csrf_token_from_somewhere)
+    block
+
+html
+  body
+    +form('GET', '/search')
+      input(type='text', name='query', placeholder='Search')
+      input(type='submit', value='Search')
+
+html
+  body
+    +form('POST', '/search')
+      input(type='text', name='query', placeholder='Search')
+      input(type='submit', value='Search')
+
+html
+  body
+    +form('POST', '/search')
+
+mixin bar()
+  #bar
+    block
+
+mixin foo()
+  #foo
+    +bar
+      block
+
++foo
+  p one
+  p two
+  p three
+
+
+mixin baz
+    #baz
+        block
+
++baz()= '123'
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.merge.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.merge.expected.json
new file mode 100644
index 0000000..0f1353d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.merge.expected.json
@@ -0,0 +1,52 @@
+{"type":"mixin","line":1,"val":"foo","args":null}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"class","line":2,"val":"bar"}
+{"type":"&attributes","line":2,"val":"attributes"}
+{"type":"text","line":2,"val":"One"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"class","line":3,"val":"baz"}
+{"type":"class","line":3,"val":"quux"}
+{"type":"&attributes","line":3,"val":"attributes"}
+{"type":"text","line":3,"val":"Two"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"&attributes","line":4,"val":"attributes"}
+{"type":"text","line":4,"val":"Three"}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"class","line":5,"val":"bar"}
+{"type":"&attributes","line":5,"val":"attributes"}
+{"type":"attrs","line":5,"attrs":[{"name":"class","val":"\"baz\"","escaped":true}]}
+{"type":"text","line":5,"val":"Four"}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"body","selfClosing":false}
+{"type":"indent","line":8,"val":2}
+{"type":"call","line":8,"val":"foo"}
+{"type":"class","line":8,"val":"hello"}
+{"type":"newline","line":9}
+{"type":"call","line":9,"val":"foo"}
+{"type":"id","line":9,"val":"world"}
+{"type":"newline","line":10}
+{"type":"call","line":10,"val":"foo"}
+{"type":"class","line":10,"val":"hello"}
+{"type":"id","line":10,"val":"world"}
+{"type":"newline","line":11}
+{"type":"call","line":11,"val":"foo"}
+{"type":"class","line":11,"val":"hello"}
+{"type":"class","line":11,"val":"world"}
+{"type":"newline","line":12}
+{"type":"call","line":12,"val":"foo"}
+{"type":"attrs","line":12,"attrs":[{"name":"class","val":"\"hello\"","escaped":true}]}
+{"type":"newline","line":13}
+{"type":"call","line":13,"val":"foo"}
+{"type":"class","line":13,"val":"hello"}
+{"type":"attrs","line":13,"attrs":[{"name":"class","val":"\"world\"","escaped":true}]}
+{"type":"newline","line":14}
+{"type":"call","line":14,"val":"foo"}
+{"type":"newline","line":15}
+{"type":"call","line":15,"val":"foo"}
+{"type":"&attributes","line":15,"val":"{class: \"hello\"}"}
+{"type":"outdent","line":15}
+{"type":"eos","line":15}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.merge.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.merge.jade
new file mode 100644
index 0000000..f0d217d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixin.merge.jade
@@ -0,0 +1,15 @@
+mixin foo
+  p.bar&attributes(attributes) One
+  p.baz.quux&attributes(attributes) Two
+  p&attributes(attributes) Three
+  p.bar&attributes(attributes)(class="baz") Four
+
+body
+  +foo.hello
+  +foo#world
+  +foo.hello#world
+  +foo.hello.world
+  +foo(class="hello")
+  +foo.hello(class="world")
+  +foo
+  +foo&attributes({class: "hello"})
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins-unused.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins-unused.expected.json
new file mode 100644
index 0000000..782a577
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins-unused.expected.json
@@ -0,0 +1,7 @@
+{"type":"mixin","line":1,"val":"never-called","args":null}
+{"type":"indent","line":2,"val":2}
+{"type":"class","line":2,"val":"wtf"}
+{"type":"text","line":2,"val":"This isn't something we ever want to output"}
+{"type":"outdent","line":3}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins-unused.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins-unused.jade
new file mode 100644
index 0000000..b0af6cc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins-unused.jade
@@ -0,0 +1,3 @@
+mixin never-called
+  .wtf This isn't something we ever want to output
+body
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.expected.json
new file mode 100644
index 0000000..ecfb4d6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.expected.json
@@ -0,0 +1,67 @@
+{"type":"mixin","line":1,"val":"comment","args":"title, str"}
+{"type":"indent","line":2,"val":2}
+{"type":"class","line":2,"val":"comment"}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h2","selfClosing":false}
+{"type":"code","line":3,"val":"title","escape":true,"buffer":true}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"class","line":4,"val":"body"}
+{"type":"code","line":4,"val":"str","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"mixin","line":7,"val":"comment","args":"title, str"}
+{"type":"indent","line":8,"val":2}
+{"type":"class","line":8,"val":"comment"}
+{"type":"indent","line":9,"val":4}
+{"type":"tag","line":9,"val":"h2","selfClosing":false}
+{"type":"code","line":9,"val":"title","escape":true,"buffer":true}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"class","line":10,"val":"body"}
+{"type":"code","line":10,"val":"str","escape":true,"buffer":true}
+{"type":"outdent","line":12}
+{"type":"outdent","line":12}
+{"type":"id","line":12,"val":"user"}
+{"type":"indent","line":13,"val":2}
+{"type":"tag","line":13,"val":"h1","selfClosing":false}
+{"type":"text","line":13,"val":"Tobi"}
+{"type":"newline","line":14}
+{"type":"class","line":14,"val":"comments"}
+{"type":"indent","line":15,"val":4}
+{"type":"call","line":15,"val":"comment","args":"'This',\n            (('is regular, javascript'))"}
+{"type":"outdent","line":17}
+{"type":"outdent","line":17}
+{"type":"mixin","line":17,"val":"list","args":null}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"ul","selfClosing":false}
+{"type":"indent","line":19,"val":4}
+{"type":"tag","line":19,"val":"li","selfClosing":false}
+{"type":"text","line":19,"val":"foo"}
+{"type":"newline","line":20}
+{"type":"tag","line":20,"val":"li","selfClosing":false}
+{"type":"text","line":20,"val":"bar"}
+{"type":"newline","line":21}
+{"type":"tag","line":21,"val":"li","selfClosing":false}
+{"type":"text","line":21,"val":"baz"}
+{"type":"outdent","line":23}
+{"type":"outdent","line":23}
+{"type":"tag","line":23,"val":"body","selfClosing":false}
+{"type":"indent","line":24,"val":2}
+{"type":"mixin","line":24,"val":"list","args":null}
+{"type":"newline","line":25}
+{"type":"call","line":25,"val":"list","args":""}
+{"type":"newline","line":26}
+{"type":"call","line":26,"val":"list","args":""}
+{"type":"outdent","line":28}
+{"type":"mixin","line":28,"val":"foobar","args":"str"}
+{"type":"indent","line":29,"val":2}
+{"type":"tag","line":29,"val":"div","selfClosing":false}
+{"type":"id","line":29,"val":"interpolation"}
+{"type":"code","line":29,"val":"str + 'interpolated'","escape":true,"buffer":true}
+{"type":"outdent","line":31}
+{"type":"code","line":31,"val":"var suffix = \"bar\"","escape":false,"buffer":false}
+{"type":"newline","line":32}
+{"type":"call","line":32,"val":"#{'foo' + suffix}","args":"'This is '"}
+{"type":"newline","line":33}
+{"type":"eos","line":33}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.jade
new file mode 100644
index 0000000..0d79eb5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.jade
@@ -0,0 +1,33 @@
+mixin comment(title, str)
+  .comment
+    h2= title
+    p.body= str
+
+
+mixin comment (title, str)
+  .comment
+    h2= title
+    p.body= str
+
+#user
+  h1 Tobi
+  .comments
+    +comment('This',
+            (('is regular, javascript')))
+
+mixin list
+  ul
+    li foo
+    li bar
+    li baz
+
+body
+  mixin list()
+  +list()
+  + list()
+
+mixin foobar(str)
+  div#interpolation= str + 'interpolated'
+
+- var suffix = "bar"
++#{'foo' + suffix}('This is ')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.rest-args.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.rest-args.expected.json
new file mode 100644
index 0000000..182aad7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.rest-args.expected.json
@@ -0,0 +1,14 @@
+{"type":"mixin","line":1,"val":"list","args":"tag, ...items"}
+{"type":"indent","line":2,"val":2}
+{"type":"interpolation","line":2,"val":"tag"}
+{"type":"indent","line":3,"val":4}
+{"type":"each","line":3,"val":"item","code":"items"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"li","selfClosing":false}
+{"type":"code","line":4,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"call","line":6,"val":"list","args":"'ul', 1, 2, 3, 4"}
+{"type":"newline","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.rest-args.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.rest-args.jade
new file mode 100644
index 0000000..929a927
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/mixins.rest-args.jade
@@ -0,0 +1,6 @@
+mixin list(tag, ...items)
+  #{tag}
+    each item in items
+      li= item
+
++list('ul', 1, 2, 3, 4)
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/namespaces.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/namespaces.expected.json
new file mode 100644
index 0000000..1c73230
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/namespaces.expected.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"fb:user:role","selfClosing":false}
+{"type":"text","line":1,"val":"Something"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"foo","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"fb:foo","val":"'bar'","escaped":true}]}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/namespaces.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/namespaces.jade
new file mode 100644
index 0000000..0694677
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/namespaces.jade
@@ -0,0 +1,2 @@
+fb:user:role Something
+foo(fb:foo='bar')
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/nesting.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/nesting.expected.json
new file mode 100644
index 0000000..b160663
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/nesting.expected.json
@@ -0,0 +1,23 @@
+{"type":"tag","line":1,"val":"ul","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"li","selfClosing":false}
+{"type":"text","line":2,"val":"a"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"li","selfClosing":false}
+{"type":"text","line":3,"val":"b"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"li","selfClosing":false}
+{"type":"indent","line":5,"val":4}
+{"type":"tag","line":5,"val":"ul","selfClosing":false}
+{"type":"indent","line":6,"val":8}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"text","line":6,"val":"c"}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"d"}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"tag","line":8,"val":"li","selfClosing":false}
+{"type":"text","line":8,"val":"e"}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/nesting.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/nesting.jade
new file mode 100644
index 0000000..f8cab4d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/nesting.jade
@@ -0,0 +1,8 @@
+ul
+  li a
+  li b
+  li
+    ul
+        li c
+        li d
+  li e
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/pre.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/pre.expected.json
new file mode 100644
index 0000000..c019cc4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/pre.expected.json
@@ -0,0 +1,25 @@
+{"type":"tag","line":1,"val":"pre","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"foo"}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"bar"}
+{"type":"newline","line":4}
+{"type":"text","line":4,"val":"baz"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"end-pipeless-text","line":5}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"pre","selfClosing":false}
+{"type":"indent","line":7,"val":2}
+{"type":"tag","line":7,"val":"code","selfClosing":false}
+{"type":"dot","line":7}
+{"type":"start-pipeless-text","line":7}
+{"type":"text","line":8,"val":"foo"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"bar"}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":"baz"}
+{"type":"end-pipeless-text","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/pre.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/pre.jade
new file mode 100644
index 0000000..75673c5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/pre.jade
@@ -0,0 +1,10 @@
+pre.
+  foo
+  bar
+  baz
+
+pre
+  code.
+    foo
+    bar
+    baz
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/quotes.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/quotes.expected.json
new file mode 100644
index 0000000..c4ba8ac
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/quotes.expected.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"\"foo\""}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"text","line":2,"val":"'foo'"}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/quotes.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/quotes.jade
new file mode 100644
index 0000000..499c835
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/quotes.jade
@@ -0,0 +1,2 @@
+p "foo"
+p 'foo'
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/regression.784.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/regression.784.expected.json
new file mode 100644
index 0000000..ed9581d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/regression.784.expected.json
@@ -0,0 +1,5 @@
+{"type":"code","line":1,"val":"var url = 'http://www.google.com'","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"class","line":2,"val":"url"}
+{"type":"text","line":2,"val":"#{url.replace('http://', '').replace(/^www\\./, '')}"}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/regression.784.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/regression.784.jade
new file mode 100644
index 0000000..bab7540
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/regression.784.jade
@@ -0,0 +1,2 @@
+- var url = 'http://www.google.com'
+.url #{url.replace('http://', '').replace(/^www\./, '')}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/script.whitespace.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/script.whitespace.expected.json
new file mode 100644
index 0000000..65105d5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/script.whitespace.expected.json
@@ -0,0 +1,14 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"if (foo) {"}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"  "}
+{"type":"newline","line":4}
+{"type":"text","line":4,"val":"  bar();"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"}"}
+{"type":"end-pipeless-text","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/script.whitespace.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/script.whitespace.jade
new file mode 100644
index 0000000..e0afc3a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/script.whitespace.jade
@@ -0,0 +1,6 @@
+script.
+  if (foo) {
+    
+    bar();
+  
+  }
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.expected.json
new file mode 100644
index 0000000..ee2753b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.expected.json
@@ -0,0 +1,20 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"if (foo) {"}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"  bar();"}
+{"type":"newline","line":4}
+{"type":"text","line":4,"val":"}"}
+{"type":"end-pipeless-text","line":4}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"code","line":5,"val":"'foo()'","escape":false,"buffer":true}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"text","line":6,"val":"foo()"}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"script","selfClosing":false}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"div","selfClosing":false}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.jade
new file mode 100644
index 0000000..d28887f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.jade
@@ -0,0 +1,8 @@
+script.
+  if (foo) {
+    bar();
+  }
+script!= 'foo()'
+script foo()
+script
+div
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.non-js.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.non-js.expected.json
new file mode 100644
index 0000000..d28fc21
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.non-js.expected.json
@@ -0,0 +1,25 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"id","line":1,"val":"user-template"}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"id","line":2,"val":"user"}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"text","line":3,"val":"<%= user.name %>"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"<%= user.description %>"}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"id","line":6,"val":"user-template"}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'text/template'","escaped":true}]}
+{"type":"dot","line":6}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"if (foo) {"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"  bar();"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"}"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.non-js.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.non-js.jade
new file mode 100644
index 0000000..9f9a408
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/scripts.non-js.jade
@@ -0,0 +1,9 @@
+script#user-template(type='text/template')
+  #user
+    h1 <%= user.name %>
+    p <%= user.description %>
+
+script#user-template(type='text/template').
+  if (foo) {
+    bar();
+  }
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/single-period.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/single-period.expected.json
new file mode 100644
index 0000000..c776758
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/single-period.expected.json
@@ -0,0 +1,3 @@
+{"type":"tag","line":1,"val":"span","selfClosing":false}
+{"type":"text","line":1,"val":"."}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/single-period.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/single-period.jade
new file mode 100644
index 0000000..f3d734c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/single-period.jade
@@ -0,0 +1 @@
+span .
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/source.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/source.expected.json
new file mode 100644
index 0000000..97f3119
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/source.expected.json
@@ -0,0 +1,13 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"audio","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"preload","val":"'auto'","escaped":true},{"name":"autobuffer","val":true,"escaped":false},{"name":"controls","val":true,"escaped":false}]}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"source","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"src","val":"'foo'","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"source","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'bar'","escaped":true}]}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/source.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/source.jade
new file mode 100644
index 0000000..db22b80
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/source.jade
@@ -0,0 +1,4 @@
+html
+  audio(preload='auto', autobuffer, controls)
+    source(src='foo')
+    source(src='bar')
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/styles.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/styles.expected.json
new file mode 100644
index 0000000..4f222b5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/styles.expected.json
@@ -0,0 +1,52 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"style","selfClosing":false}
+{"type":"dot","line":3}
+{"type":"start-pipeless-text","line":3}
+{"type":"text","line":4,"val":"body {"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"  padding: 50px;"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"}"}
+{"type":"end-pipeless-text","line":6}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"body","selfClosing":false}
+{"type":"indent","line":8,"val":4}
+{"type":"tag","line":8,"val":"div","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"style","val":"'color:red;background:green'","escaped":true}]}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"div","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"style","val":"{color: 'red', background: 'green'}","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"div","selfClosing":false}
+{"type":"&attributes","line":10,"val":"{style: 'color:red;background:green'}"}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"div","selfClosing":false}
+{"type":"&attributes","line":11,"val":"{style: {color: 'red', background: 'green'}}"}
+{"type":"newline","line":12}
+{"type":"mixin","line":12,"val":"div","args":null}
+{"type":"indent","line":13,"val":6}
+{"type":"tag","line":13,"val":"div","selfClosing":false}
+{"type":"&attributes","line":13,"val":"attributes"}
+{"type":"outdent","line":14}
+{"type":"call","line":14,"val":"div"}
+{"type":"attrs","line":14,"attrs":[{"name":"style","val":"'color:red;background:green'","escaped":true}]}
+{"type":"newline","line":15}
+{"type":"call","line":15,"val":"div"}
+{"type":"attrs","line":15,"attrs":[{"name":"style","val":"{color: 'red', background: 'green'}","escaped":true}]}
+{"type":"newline","line":16}
+{"type":"code","line":16,"val":"var bg = 'green';","escape":false,"buffer":false}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"div","selfClosing":false}
+{"type":"attrs","line":17,"attrs":[{"name":"style","val":"{color: 'red', background: bg}","escaped":true}]}
+{"type":"newline","line":18}
+{"type":"tag","line":18,"val":"div","selfClosing":false}
+{"type":"&attributes","line":18,"val":"{style: {color: 'red', background: bg}}"}
+{"type":"newline","line":19}
+{"type":"call","line":19,"val":"div"}
+{"type":"attrs","line":19,"attrs":[{"name":"style","val":"{color: 'red', background: bg}","escaped":true}]}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"eos","line":20}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/styles.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/styles.jade
new file mode 100644
index 0000000..9618353
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/styles.jade
@@ -0,0 +1,19 @@
+html
+  head
+    style.
+      body {
+        padding: 50px;
+      }
+  body
+    div(style='color:red;background:green')
+    div(style={color: 'red', background: 'green'})
+    div&attributes({style: 'color:red;background:green'})
+    div&attributes({style: {color: 'red', background: 'green'}})
+    mixin div()
+      div&attributes(attributes)
+    +div(style='color:red;background:green')
+    +div(style={color: 'red', background: 'green'})
+    - var bg = 'green';
+    div(style={color: 'red', background: bg})
+    div&attributes({style: {color: 'red', background: bg}})
+    +div(style={color: 'red', background: bg})
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tag.interpolation.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tag.interpolation.expected.json
new file mode 100644
index 0000000..e431532
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tag.interpolation.expected.json
@@ -0,0 +1,56 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var tag = 'p'","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"code","line":3,"val":"var foo = 'bar'","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"interpolation","line":5,"val":"tag"}
+{"type":"text","line":5,"val":"value"}
+{"type":"newline","line":6}
+{"type":"interpolation","line":6,"val":"tag"}
+{"type":"attrs","line":6,"attrs":[{"name":"foo","val":"'bar'","escaped":true}]}
+{"type":"text","line":6,"val":"value"}
+{"type":"newline","line":7}
+{"type":"interpolation","line":7,"val":"foo ? 'a' : 'li'"}
+{"type":"attrs","line":7,"attrs":[{"name":"something","val":true,"escaped":true}]}
+{"type":"text","line":7,"val":"here"}
+{"type":"newline","line":9}
+{"type":"mixin","line":9,"val":"item","args":"icon"}
+{"type":"indent","line":10,"val":2}
+{"type":"tag","line":10,"val":"li","selfClosing":false}
+{"type":"indent","line":11,"val":4}
+{"type":"code","line":11,"val":"if ( attributes.href)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":12,"val":6}
+{"type":"tag","line":12,"val":"a","selfClosing":false}
+{"type":"&attributes","line":12,"val":"attributes"}
+{"type":"indent","line":13,"val":8}
+{"type":"tag","line":13,"val":"img","selfClosing":false}
+{"type":"class","line":13,"val":"icon"}
+{"type":"attrs","line":13,"attrs":[{"name":"src","val":"icon","escaped":true}]}
+{"type":"newline","line":14}
+{"type":"mixin-block","line":14}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"code","line":15,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":16,"val":6}
+{"type":"tag","line":16,"val":"span","selfClosing":false}
+{"type":"&attributes","line":16,"val":"attributes"}
+{"type":"indent","line":17,"val":8}
+{"type":"tag","line":17,"val":"img","selfClosing":false}
+{"type":"class","line":17,"val":"icon"}
+{"type":"attrs","line":17,"attrs":[{"name":"src","val":"icon","escaped":true}]}
+{"type":"newline","line":18}
+{"type":"mixin-block","line":18}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"tag","line":20,"val":"ul","selfClosing":false}
+{"type":"indent","line":21,"val":2}
+{"type":"call","line":21,"val":"item","args":"'contact'"}
+{"type":"text","line":21,"val":"Contact"}
+{"type":"newline","line":22}
+{"type":"call","line":22,"val":"item"}
+{"type":"attrs","line":22,"attrs":[{"name":"href","val":"'/contact'","escaped":true}]}
+{"type":"text","line":22,"val":"Contact"}
+{"type":"outdent","line":23}
+{"type":"eos","line":23}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tag.interpolation.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tag.interpolation.jade
new file mode 100644
index 0000000..d923ddb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tag.interpolation.jade
@@ -0,0 +1,22 @@
+
+- var tag = 'p'
+- var foo = 'bar'
+
+#{tag} value
+#{tag}(foo='bar') value
+#{foo ? 'a' : 'li'}(something) here
+
+mixin item(icon)
+  li
+    if attributes.href
+      a&attributes(attributes)
+        img.icon(src=icon)
+        block
+    else
+      span&attributes(attributes)
+        img.icon(src=icon)
+        block
+
+ul
+  +item('contact') Contact
+  +item(href='/contact') Contact
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tags.self-closing.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tags.self-closing.expected.json
new file mode 100644
index 0000000..e2ae701
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tags.self-closing.expected.json
@@ -0,0 +1,31 @@
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"foo","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"bar","val":"'baz'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"foo","selfClosing":true}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"foo","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"bar","val":"'baz'","escaped":true}],"selfClosing":true}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"foo","selfClosing":false}
+{"type":"text","line":7,"val":"/"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"foo","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"bar","val":"'baz'","escaped":true}]}
+{"type":"text","line":8,"val":"/"}
+{"type":"newline","line":9}
+{"type":"comment","line":9,"val":" can have a single space after them","buffer":false}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"img","selfClosing":false}
+{"type":"text","line":10,"val":" "}
+{"type":"newline","line":11}
+{"type":"comment","line":11,"val":" can have lots of white space after them","buffer":false}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"img","selfClosing":false}
+{"type":"text","line":12,"val":"   "}
+{"type":"outdent","line":12}
+{"type":"eos","line":12}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tags.self-closing.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tags.self-closing.jade
new file mode 100644
index 0000000..de327cc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/tags.self-closing.jade
@@ -0,0 +1,12 @@
+
+body
+  foo
+  foo(bar='baz')
+  foo/
+  foo(bar='baz')/
+  foo /
+  foo(bar='baz') /
+  //- can have a single space after them
+  img 
+  //- can have lots of white space after them
+  img    
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/template.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/template.expected.json
new file mode 100644
index 0000000..24b2346
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/template.expected.json
@@ -0,0 +1,23 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"article","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h2","selfClosing":false}
+{"type":"text","line":3,"val":"{{title}}"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"{{description}}"}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"dot","line":6}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"article"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"  h2 {{title}}"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"  p {{description}}"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/template.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/template.jade
new file mode 100644
index 0000000..20e086b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/template.jade
@@ -0,0 +1,9 @@
+script(type='text/x-template')
+  article
+    h2 {{title}}
+    p {{description}}
+
+script(type='text/x-template').
+  article
+    h2 {{title}}
+    p {{description}}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text-block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text-block.expected.json
new file mode 100644
index 0000000..65f7365
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text-block.expected.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"label","selfClosing":false}
+{"type":"text","line":2,"val":"Username:"}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"input","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'user[name]'","escaped":true}]}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"label","selfClosing":false}
+{"type":"text","line":5,"val":"Password:"}
+{"type":"indent","line":6,"val":2}
+{"type":"tag","line":6,"val":"input","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'user[pass]'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text-block.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text-block.jade
new file mode 100644
index 0000000..a032fa7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text-block.jade
@@ -0,0 +1,6 @@
+
+label Username:
+  input(type='text', name='user[name]')
+
+label Password:
+  input(type='text', name='user[pass]')
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text.expected.json
new file mode 100644
index 0000000..ddc2a06
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text.expected.json
@@ -0,0 +1,50 @@
+{"type":"tag","line":1,"val":"option","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"value","val":"''","escaped":true}]}
+{"type":"text","line":1,"val":"-- (selected) --"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"text","line":4,"val":"foo"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"bar"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"baz"}
+{"type":"outdent","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"dot","line":8}
+{"type":"start-pipeless-text","line":8}
+{"type":"text","line":9,"val":"foo"}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":""}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":""}
+{"type":"newline","line":12}
+{"type":"text","line":12,"val":"bar"}
+{"type":"newline","line":13}
+{"type":"text","line":13,"val":"baz"}
+{"type":"newline","line":14}
+{"type":"text","line":14,"val":""}
+{"type":"end-pipeless-text","line":14}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"pre","selfClosing":false}
+{"type":"indent","line":16,"val":2}
+{"type":"text","line":16,"val":"foo"}
+{"type":"newline","line":17}
+{"type":"text","line":17,"val":"  bar"}
+{"type":"newline","line":18}
+{"type":"text","line":18,"val":"    baz"}
+{"type":"newline","line":19}
+{"type":"text","line":19,"val":"."}
+{"type":"outdent","line":21}
+{"type":"tag","line":21,"val":"pre","selfClosing":false}
+{"type":"dot","line":21}
+{"type":"start-pipeless-text","line":21}
+{"type":"text","line":22,"val":"foo"}
+{"type":"newline","line":23}
+{"type":"text","line":23,"val":"  bar"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"    baz"}
+{"type":"newline","line":25}
+{"type":"text","line":25,"val":"."}
+{"type":"end-pipeless-text","line":25}
+{"type":"eos","line":25}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text.jade
new file mode 100644
index 0000000..a26387e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/text.jade
@@ -0,0 +1,25 @@
+option(value='') -- (selected) --
+
+p
+  | foo
+  | bar
+  | baz
+
+p.
+  foo
+
+
+  bar
+  baz
+
+pre
+  | foo
+  |   bar
+  |     baz
+  | .
+
+pre.
+  foo
+    bar
+      baz
+  .
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/utf8bom.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/utf8bom.expected.json
new file mode 100644
index 0000000..90f8d9c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/utf8bom.expected.json
@@ -0,0 +1,4 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"\"foo\""}
+{"type":"newline","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/utf8bom.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/utf8bom.jade
new file mode 100644
index 0000000..9a32814
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/utf8bom.jade
@@ -0,0 +1 @@
+﻿p "foo"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/vars.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/vars.expected.json
new file mode 100644
index 0000000..df2aec1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/vars.expected.json
@@ -0,0 +1,7 @@
+{"type":"code","line":1,"val":"var foo = 'bar'","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var list = [1,2,3]","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"class","val":"list","escaped":true},{"name":"id","val":"foo","escaped":true}]}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/vars.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/vars.jade
new file mode 100644
index 0000000..46451a9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/vars.jade
@@ -0,0 +1,3 @@
+- var foo = 'bar'
+- var list = [1,2,3]
+a(class=list, id=foo)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/while.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/while.expected.json
new file mode 100644
index 0000000..5b3625d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/while.expected.json
@@ -0,0 +1,13 @@
+{"type":"code","line":1,"val":"var x = 1;","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"ul","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"code","line":3,"val":"while (x < 10)","requiresBlock":true}
+{"type":"indent","line":4,"val":4}
+{"type":"code","line":4,"val":"x++;","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"code","line":5,"val":"x","escape":true,"buffer":true}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/while.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/while.jade
new file mode 100644
index 0000000..059b54b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/while.jade
@@ -0,0 +1,5 @@
+- var x = 1;
+ul
+  while x < 10
+    - x++;
+    li= x
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/xml.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/xml.expected.json
new file mode 100644
index 0000000..2ba3ad2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/xml.expected.json
@@ -0,0 +1,8 @@
+{"type":"doctype","line":1,"val":"xml"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"category","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"term","val":"'some term'","escaped":true}],"selfClosing":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"link","selfClosing":false}
+{"type":"text","line":3,"val":"http://google.com"}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/xml.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/xml.jade
new file mode 100644
index 0000000..2b21fa4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/xml.jade
@@ -0,0 +1,3 @@
+doctype xml
+category(term='some term')/
+link http://google.com
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional-head.expected.json
new file mode 100644
index 0000000..a5a09dd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional-head.expected.json
@@ -0,0 +1,14 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"script","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"src","val":"'/jquery.js'","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"yield","line":3}
+{"type":"newline","line":4}
+{"type":"code","line":4,"val":"if ( false)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":5,"val":4}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/jquery.ui.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional-head.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional-head.jade
new file mode 100644
index 0000000..8515406
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional-head.jade
@@ -0,0 +1,5 @@
+head
+  script(src='/jquery.js')
+  yield
+  if false
+    script(src='/jquery.ui.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional.expected.json
new file mode 100644
index 0000000..17eb97d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional.expected.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"yield-before-conditional-head"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/caustic.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/app.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional.jade
new file mode 100644
index 0000000..eedc33c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-before-conditional.jade
@@ -0,0 +1,5 @@
+html
+  body
+    include yield-before-conditional-head
+      script(src='/caustic.js')
+      script(src='/app.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-head.expected.json
new file mode 100644
index 0000000..bda3202
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-head.expected.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"script","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"src","val":"'/jquery.js'","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"yield","line":3}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/jquery.ui.js'","escaped":true}]}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-head.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-head.jade
new file mode 100644
index 0000000..1428be6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-head.jade
@@ -0,0 +1,4 @@
+head
+  script(src='/jquery.js')
+  yield
+  script(src='/jquery.ui.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title-head.expected.json
new file mode 100644
index 0000000..69f4114
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title-head.expected.json
@@ -0,0 +1,13 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"title","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"yield","line":3}
+{"type":"outdent","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/jquery.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/jquery.ui.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title-head.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title-head.jade
new file mode 100644
index 0000000..5ec7d32
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title-head.jade
@@ -0,0 +1,5 @@
+head
+  title
+    yield
+  script(src='/jquery.js')
+  script(src='/jquery.ui.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title.expected.json
new file mode 100644
index 0000000..451c04d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title.expected.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"yield-title-head"}
+{"type":"indent","line":4,"val":6}
+{"type":"text","line":4,"val":"My Title"}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title.jade
new file mode 100644
index 0000000..0754f52
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield-title.jade
@@ -0,0 +1,4 @@
+html
+  body
+    include yield-title-head
+      | My Title
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield.expected.json
new file mode 100644
index 0000000..f88cba9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield.expected.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"yield-head"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/caustic.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/app.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield.jade
new file mode 100644
index 0000000..bc6a679
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/cases/yield.jade
@@ -0,0 +1,5 @@
+html
+  body
+    include yield-head
+      script(src='/caustic.js')
+      script(src='/app.js')
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/index.js
new file mode 100644
index 0000000..5a88295
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/index.js
@@ -0,0 +1,18 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var lex = require('../');
+
+var dir = __dirname + '/cases/';
+fs.readdirSync(dir).forEach(function (testCase) {
+  if (/\.jade$/.test(testCase)) {
+    console.dir(testCase);
+    var expected = fs.readFileSync(dir + testCase.replace(/\.jade$/, '.expected.json'), 'utf8')
+                     .split(/\n/).map(JSON.parse);
+    var result = lex(fs.readFileSync(dir + testCase, 'utf8'), __dirname + '/cases/' + testCase);
+    fs.writeFileSync(dir + testCase.replace(/\.jade$/, '.actual.json'),
+                     result.map(JSON.stringify).join('\n'));
+    assert.deepEqual(expected, result);
+  }
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/update-test-cases.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/update-test-cases.js
new file mode 100644
index 0000000..9a41644
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-lexer/test/update-test-cases.js
@@ -0,0 +1,21 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var lex = require('../');
+
+var dir = __dirname + '/cases/';
+fs.readdirSync(dir).forEach(function (testCase) {
+  if (/\.jade$/.test(testCase)) {
+    var expected = fs.readFileSync(dir + testCase.replace(/\.jade$/, '.expected.json'), 'utf8')
+                     .split(/\n/).map(JSON.parse);
+    var result = lex(fs.readFileSync(dir + testCase, 'utf8'), __dirname + '/cases/' + testCase);
+    try {
+      assert.deepEqual(expected, result);
+    } catch (ex) {
+      console.log('Updating ' + testCase);
+      fs.writeFileSync(dir + testCase.replace(/\.jade$/, '.expected.json'),
+                       result.map(JSON.stringify).join('\n'));
+    }
+  }
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/.npmignore
new file mode 100644
index 0000000..b83202d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/.npmignore
@@ -0,0 +1,13 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/README.md
new file mode 100644
index 0000000..81781fe
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/README.md
@@ -0,0 +1,15 @@
+# jade-linker
+
+Link multiple jade ASTs together using include/extends
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-linker/master.svg)](https://travis-ci.org/jadejs/jade-linker)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-linker.svg)](https://gemnasium.com/jadejs/jade-linker)
+[![NPM version](https://img.shields.io/npm/v/jade-linker.svg)](https://www.npmjs.org/package/jade-linker)
+
+## Installation
+
+    npm install jade-linker
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/index.js
new file mode 100644
index 0000000..b7ba2ce
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/index.js
@@ -0,0 +1,144 @@
+'use strict';
+
+var assert = require('assert');
+var walk = require('jade-walk');
+
+module.exports = link;
+function link(ast) {
+  assert(ast.type === 'Block', 'The top level element should always be a block');
+  var extendsNode = null;
+  if (ast.nodes[0].type === 'Extends') {
+    extendsNode = ast.nodes.shift();
+  }
+  ast = applyIncludes(ast);
+  ast.declaredBlocks = findDeclaredBlocks(ast);
+  if (extendsNode) {
+    var mixins = [];
+    var expectedBlocks = [];
+    for (var i = 0; i < ast.nodes.length; i++) {
+      if (ast.nodes[i].type === 'NamedBlock') {
+        expectedBlocks.push(ast.nodes[i].name);
+      } else if (ast.nodes[i].type === 'Mixin' && ast.nodes[i].call === false) {
+        mixins.push(ast.nodes[i]);
+      } else {
+        throw new Error('Only named blocks and mixins can appear at the top level of an extending template');
+      }
+    }    
+    var parent = link(extendsNode.file.ast);
+    extend(parent.declaredBlocks, ast);
+    walk(parent, function (node) {
+      if (node.type === 'NamedBlock') {
+        expectedBlocks = expectedBlocks.filter(function (name) {
+          return name !== node.name;
+        });
+      }
+    });
+    if (expectedBlocks.length !== 0) {
+      //throw new Error('Unexpected blocks');
+      expectedBlocks.forEach(function (expectedBlock) {
+        console.warn('Warning: Unexpected block ' + expectedBlock.name + ' on line ' + expectedBlock.line + ' + of ' + expectedBlock.filename + '. This warning will be an error in v2.0.0');
+      });
+    }
+    Object.keys(ast.declaredBlocks).forEach(function (name) {
+      parent.declaredBlocks[name] = ast.declaredBlocks[name];
+    });
+    parent.nodes = mixins.concat(parent.nodes);
+    return parent;
+  }
+  return ast;
+}
+
+function findDeclaredBlocks(ast) {
+  var definitions = {};
+  var stack = [];
+  walk(ast, function before(node) {
+    if (node.type === 'NamedBlock' && node.mode === 'replace') {
+      stack.push(node.name);
+      definitions[node.name] = node;
+    }
+  }, function after(node) {
+    if (node.type === 'NamedBlock' && node.mode === 'replace') {
+      stack.pop();
+    }
+  });
+  return definitions;
+}
+function extend(parentBlocks, ast) {
+  var stack = {};
+  walk(ast, function before(node) {
+    if (node.type === 'NamedBlock') {
+      if (stack[node.name] === node.name) {
+        return node.ignore = true;
+      }
+      stack[node.name] = node.name;
+      var parentBlock = parentBlocks[node.name];
+      if (parentBlock) {
+        if (parentBlock.parent) parentBlock = parentBlock.parent;
+        node.parent = parentBlock;
+        switch (node.mode) {
+          case 'append':
+            parentBlock.nodes = parentBlock.nodes.concat(node.nodes);
+            break;
+          case 'prepend':
+            parentBlock.nodes = node.nodes.concat(parentBlock.nodes);
+            break;
+          case 'replace':
+            parentBlock.nodes = node.nodes;
+            break;
+        }
+      }
+    }
+  }, function after(node) {
+    if (node.type === 'NamedBlock' && !node.ignore) {
+      delete stack[node.name];
+    }
+  });
+}
+function applyIncludes(ast, child) {
+  return walk(ast, function before(node) {
+
+  }, function after(node, replace) {
+    if (node.type === 'Include') {
+      if (node.filter) {
+        replace({
+          type: 'Filter',
+          name: node.filter,
+          block: {type: 'Block', nodes: [ {type: 'Text', val: node.file.str.replace(/\r/g, '')} ] },
+          attrs: node.attrs,
+          filename: node.fullPath
+        });
+      } else if (/\.jade$/.test(node.file.fullPath)) {
+        replace(applyYield(link(node.file.ast), node.block));
+      } else {
+        replace({type: 'Text', val: node.file.str.replace(/\r/g, '')});
+      }
+    }
+  });
+}
+function applyYield(ast, block) {
+  if (!block || !block.nodes.length) return ast;
+  var replaced = false;
+  ast = walk(ast, null, function (node, replace) {
+    if (node.type === 'Block' && node.yield === true) {
+      replaced = true;
+      node.nodes.push(block);
+    }
+  });
+  function defaultYieldLocation(node) {
+    var res = node;
+    for (var i = 0; i < node.nodes.length; i++) {
+      if (node.nodes[i].textOnly) continue;
+      if (node.nodes[i].type === 'Block') {
+        res = defaultYieldLocation(node.nodes[i]);
+      } else if (node.nodes[i].block && node.nodes[i].block.nodes.length) {
+        res = defaultYieldLocation(node.nodes[i].block);
+      }
+    }
+    return res;
+  }
+  if (!replaced) {
+    // todo: probably should deprecate this with a warning
+    defaultYieldLocation(ast).nodes.push(block);
+  }
+  return ast;
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/.npmignore
new file mode 100644
index 0000000..b83202d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/.npmignore
@@ -0,0 +1,13 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/README.md
new file mode 100644
index 0000000..a590f1f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/README.md
@@ -0,0 +1,37 @@
+# jade-walk
+
+Walk and transform a jade AST
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-walk/master.svg)](https://travis-ci.org/jadejs/jade-walk)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-walk.svg)](https://gemnasium.com/jadejs/jade-walk)
+[![NPM version](https://img.shields.io/npm/v/jade-walk.svg)](https://www.npmjs.org/package/jade-walk)
+
+## Installation
+
+    npm install jade-walk
+
+## Usage
+
+
+```js
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var walk = require('jade-walk');
+
+var ast = walk(parse(lex('.my-class foo')), function before(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+  // return `false` to skip descending
+  if (node.type === 'Text') {
+    replace({ type: 'Text', val: 'bar', line: node.line });
+  }
+}, function after(node, replace) {
+  // called after walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+}, {includeDependencies: true});
+assert.deepEqual(parse(lex('.my-class bar')), ast);
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/index.js
new file mode 100644
index 0000000..29289a9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/index.js
@@ -0,0 +1,58 @@
+'use strict';
+
+module.exports = walkAST;
+function walkAST(ast, before, after, options) {
+  if (after && typeof after === 'object' && typeof options === 'undefined') {
+    options = after;
+    after = null;
+  }
+  options = options || {includeDependencies: false};
+  function replace(replacement) {
+    ast = replacement;
+  }
+  var result = before && before(ast, replace);
+  if (before && result === false) {
+    return ast;
+  }
+  switch (ast.type) {
+    case 'NamedBlock':
+    case 'Block':
+      ast.nodes = ast.nodes.map(function (node) {
+        return walkAST(node, before, after, options);
+      });
+      break;
+    case 'Case':
+    case 'Each':
+    case 'Mixin':
+    case 'Tag':
+    case 'When':
+    case 'Code':
+      if (ast.block) {
+        ast.block = walkAST(ast.block, before, after, options);
+      }
+      break;
+    case 'Extends':
+    case 'Include':
+      walkAST(ast.file, before, after, options);
+      break;
+    case 'Attrs':
+    case 'BlockComment':
+    case 'Comment':
+    case 'Doctype':
+    case 'Filter':
+    case 'Literal':
+    case 'MixinBlock':
+    case 'Text':
+      break;
+    case 'FileReference':
+      if (options.includeDependencies && ast.ast) {
+        walkAST(ast.ast, before, after, options);
+      }
+      break;
+    default:
+      throw new Error('Unexpected node type ' + ast.type);
+      break;
+  }
+  after && after(ast, replace);
+  return ast;
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/package.json
new file mode 100644
index 0000000..e6aa95a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/package.json
@@ -0,0 +1,50 @@
+{
+  "name": "jade-walk",
+  "version": "0.0.2",
+  "description": "Walk and transform a jade AST",
+  "keywords": [
+    "jade"
+  ],
+  "dependencies": {},
+  "devDependencies": {
+    "jade-lexer": "0.0.5",
+    "jade-parser": "0.0.7"
+  },
+  "scripts": {
+    "test": "node test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-walk.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "7589c5862d50b01b00256a3c034a7d6bbf1b8a14",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-walk/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-walk",
+  "_id": "jade-walk@0.0.2",
+  "_shasum": "d32518f3a273ddbca32827272b46a1d96a1c1f64",
+  "_from": "jade-walk@0.0.2",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "d32518f3a273ddbca32827272b46a1d96a1c1f64",
+    "tarball": "http://registry.npmjs.org/jade-walk/-/jade-walk-0.0.2.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-walk/-/jade-walk-0.0.2.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/test.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/test.js
new file mode 100644
index 0000000..899534b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/node_modules/jade-walk/test.js
@@ -0,0 +1,20 @@
+'use strict';
+
+var assert = require('assert');
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var walk = require('./');
+
+var ast = walk(parse(lex('.my-class foo')), function before(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+  // return `false` to skip descending
+  if (node.type === 'Text') {
+    replace({ type: 'Text', val: 'bar', line: node.line });
+  }
+}, function after(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+});
+assert.deepEqual(parse(lex('.my-class bar')), ast);
+console.log('tests passed');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/package.json
new file mode 100644
index 0000000..ee55619
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-linker/package.json
@@ -0,0 +1,49 @@
+{
+  "name": "jade-linker",
+  "version": "0.0.1",
+  "description": "Link multiple jade ASTs together using include/extends",
+  "keywords": [],
+  "dependencies": {
+    "jade-walk": "0.0.2"
+  },
+  "devDependencies": {
+    "mocha": "*"
+  },
+  "scripts": {
+    "test": "mocha -R spec"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-linker.git"
+  },
+  "author": {
+    "name": "Forbes Lindesay"
+  },
+  "license": "MIT",
+  "gitHead": "86935bc435896a0934654e3c7565dade514a3cae",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-linker/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-linker",
+  "_id": "jade-linker@0.0.1",
+  "_shasum": "1be18034509cb7b467017dc9043f8a2c56fab014",
+  "_from": "jade-linker@*",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "1be18034509cb7b467017dc9043f8a2c56fab014",
+    "tarball": "http://registry.npmjs.org/jade-linker/-/jade-linker-0.0.1.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-linker/-/jade-linker-0.0.1.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/.npmignore
new file mode 100644
index 0000000..9ec7ae0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/.npmignore
@@ -0,0 +1,14 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
+/test/output.json
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/README.md
new file mode 100644
index 0000000..23bed37
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/README.md
@@ -0,0 +1,47 @@
+# jade-load
+
+The jade loader is responsible for loading the depenendencies of a given jade file.  It adds `fullPath` and `str` properties to every `Include` and `Extends` node.  It also adds an `ast` property to any `Include` nodes that are loading jade and any `Extends` nodes.  It then recursively loads the dependencies of any of those included files.
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-load/master.svg)](https://travis-ci.org/jadejs/jade-load)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-load.svg)](https://gemnasium.com/jadejs/jade-load)
+[![NPM version](https://img.shields.io/npm/v/jade-load.svg)](https://www.npmjs.org/package/jade-load)
+
+## Installation
+
+    npm install jade-load
+
+## Usage
+
+```js
+var fs = require('fs');
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var load = require('jade-load');
+
+// you can do everything very manually
+
+var str = fs.readFileSync('bar.jade', 'utf8');
+var ast = load(parse(lex(str, 'bar.jade'), 'bar.jade'), {
+  lex: lex,
+  parse: parse
+});
+
+// or you can do all that in just two steps
+
+var str = fs.readFileSync('bar.jade', 'utf8');
+var ast = load.string(str, 'bar.jade', {
+  lex: lex,
+  parse: parse
+});
+
+// or you can do all that in only one step
+
+var ast = load.file('bar.jade', {
+  lex: lex,
+  parse: parse
+});
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/index.js
new file mode 100644
index 0000000..2101031
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/index.js
@@ -0,0 +1,83 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var walk = require('jade-walk');
+
+module.exports = load;
+function load(ast, options) {
+  load.validateOptions(options);
+  // clone the ast
+  ast = JSON.parse(JSON.stringify(ast));
+  return walk(ast, function (node) {
+    if (node.str === undefined) {
+      if (node.type === 'Include' || node.type === 'Extends') {
+        var file = node.file;
+        if (file.type !== 'FileReference') {
+          throw new Error('Expected file.type to be "FileReference"');
+        }
+        var path, str;
+        try {
+          path = load.resolve(file.path, file.filename, options);
+          file.fullPath = path;
+          str = load.read(path, options);
+        } catch (ex) {
+          ex.message += '\n    at ' + node.filename + ' line ' + node.line;
+          throw ex;
+        }
+        file.str = str;
+        if (node.type === 'Extends' || (!node.filter && /\.jade$/.test(path))) {
+          file.ast = load.string(str, path, options);
+        }
+      }
+    }
+  });
+}
+
+load.string = function loadString(str, filename, options) {
+  load.validateOptions(options);
+  var tokens = options.lex(str, filename);
+  var ast = options.parse(tokens, filename);
+  return load(ast, options);
+};
+load.file = function loadFile(filename, options) {
+  load.validateOptions(options);
+  var str = load.read(filename, options);
+  return load.string(str, filename, options);
+}
+
+load.resolve = function resolve(filename, source, options) {
+  if (options && options.resolve) return options.resolve(filename, source, options);
+  filename = filename.trim();
+  source = source.trim();
+  if (filename[0] !== '/' && !source)
+    throw new Error('the "filename" option is required to use includes and extends with "relative" paths');
+
+  if (filename[0] === '/' && !options.basedir)
+    throw new Error('the "basedir" option is required to use includes and extends with "absolute" paths');
+
+  filename = path.join(filename[0] === '/' ? options.basedir : path.dirname(source), filename);
+
+  if (path.basename(filename).indexOf('.') === -1) filename += '.jade';
+
+  return filename;
+};
+load.read = function read(filename, options) {
+  if (options && options.read) return options.read(filename, options);
+  return fs.readFileSync(filename, 'utf8');
+};
+
+load.validateOptions = function validateOptions(options) {
+  if (typeof options.lex !== 'function') {
+    throw new TypeError('options.lex must be a function');
+  }
+  if (typeof options.parse !== 'function') {
+    throw new TypeError('options.parse must be a function');
+  }
+  if (options.resolve && typeof options.resolve !== 'function') {
+    throw new TypeError('options.resolve must be a function');
+  }
+  if (options.read && typeof options.read !== 'function') {
+    throw new TypeError('options.read must be a function');
+  }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/.npmignore
new file mode 100644
index 0000000..b83202d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/.npmignore
@@ -0,0 +1,13 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/README.md
new file mode 100644
index 0000000..a590f1f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/README.md
@@ -0,0 +1,37 @@
+# jade-walk
+
+Walk and transform a jade AST
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-walk/master.svg)](https://travis-ci.org/jadejs/jade-walk)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-walk.svg)](https://gemnasium.com/jadejs/jade-walk)
+[![NPM version](https://img.shields.io/npm/v/jade-walk.svg)](https://www.npmjs.org/package/jade-walk)
+
+## Installation
+
+    npm install jade-walk
+
+## Usage
+
+
+```js
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var walk = require('jade-walk');
+
+var ast = walk(parse(lex('.my-class foo')), function before(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+  // return `false` to skip descending
+  if (node.type === 'Text') {
+    replace({ type: 'Text', val: 'bar', line: node.line });
+  }
+}, function after(node, replace) {
+  // called after walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+}, {includeDependencies: true});
+assert.deepEqual(parse(lex('.my-class bar')), ast);
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/index.js
new file mode 100644
index 0000000..29289a9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/index.js
@@ -0,0 +1,58 @@
+'use strict';
+
+module.exports = walkAST;
+function walkAST(ast, before, after, options) {
+  if (after && typeof after === 'object' && typeof options === 'undefined') {
+    options = after;
+    after = null;
+  }
+  options = options || {includeDependencies: false};
+  function replace(replacement) {
+    ast = replacement;
+  }
+  var result = before && before(ast, replace);
+  if (before && result === false) {
+    return ast;
+  }
+  switch (ast.type) {
+    case 'NamedBlock':
+    case 'Block':
+      ast.nodes = ast.nodes.map(function (node) {
+        return walkAST(node, before, after, options);
+      });
+      break;
+    case 'Case':
+    case 'Each':
+    case 'Mixin':
+    case 'Tag':
+    case 'When':
+    case 'Code':
+      if (ast.block) {
+        ast.block = walkAST(ast.block, before, after, options);
+      }
+      break;
+    case 'Extends':
+    case 'Include':
+      walkAST(ast.file, before, after, options);
+      break;
+    case 'Attrs':
+    case 'BlockComment':
+    case 'Comment':
+    case 'Doctype':
+    case 'Filter':
+    case 'Literal':
+    case 'MixinBlock':
+    case 'Text':
+      break;
+    case 'FileReference':
+      if (options.includeDependencies && ast.ast) {
+        walkAST(ast.ast, before, after, options);
+      }
+      break;
+    default:
+      throw new Error('Unexpected node type ' + ast.type);
+      break;
+  }
+  after && after(ast, replace);
+  return ast;
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/package.json
new file mode 100644
index 0000000..e6aa95a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/package.json
@@ -0,0 +1,50 @@
+{
+  "name": "jade-walk",
+  "version": "0.0.2",
+  "description": "Walk and transform a jade AST",
+  "keywords": [
+    "jade"
+  ],
+  "dependencies": {},
+  "devDependencies": {
+    "jade-lexer": "0.0.5",
+    "jade-parser": "0.0.7"
+  },
+  "scripts": {
+    "test": "node test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-walk.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "7589c5862d50b01b00256a3c034a7d6bbf1b8a14",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-walk/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-walk",
+  "_id": "jade-walk@0.0.2",
+  "_shasum": "d32518f3a273ddbca32827272b46a1d96a1c1f64",
+  "_from": "jade-walk@0.0.2",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "d32518f3a273ddbca32827272b46a1d96a1c1f64",
+    "tarball": "http://registry.npmjs.org/jade-walk/-/jade-walk-0.0.2.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-walk/-/jade-walk-0.0.2.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/test.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/test.js
new file mode 100644
index 0000000..899534b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/node_modules/jade-walk/test.js
@@ -0,0 +1,20 @@
+'use strict';
+
+var assert = require('assert');
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var walk = require('./');
+
+var ast = walk(parse(lex('.my-class foo')), function before(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+  // return `false` to skip descending
+  if (node.type === 'Text') {
+    replace({ type: 'Text', val: 'bar', line: node.line });
+  }
+}, function after(node, replace) {
+  // called before walking the children of `node`
+  // to replace the node, call `replace(newNode)`
+});
+assert.deepEqual(parse(lex('.my-class bar')), ast);
+console.log('tests passed');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/package.json
new file mode 100644
index 0000000..41d3054
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/package.json
@@ -0,0 +1,50 @@
+{
+  "name": "jade-load",
+  "version": "0.0.3",
+  "description": "The jade loader is responsible for loading the depenendencies of a given jade file.",
+  "keywords": [],
+  "dependencies": {
+    "jade-walk": "0.0.2"
+  },
+  "devDependencies": {
+    "jade-lexer": "0.0.5",
+    "jade-parser": "0.0.7"
+  },
+  "scripts": {
+    "test": "node test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-load.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "0dfc2f2de005d618466ee48703d4ec1fc62ad309",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-load/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-load",
+  "_id": "jade-load@0.0.3",
+  "_shasum": "12dcc8b9f7df037bac8b637a30f4de34ea9ec004",
+  "_from": "jade-load@*",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "12dcc8b9f7df037bac8b637a30f4de34ea9ec004",
+    "tarball": "http://registry.npmjs.org/jade-load/-/jade-load-0.0.3.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-load/-/jade-load-0.0.3.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/bar.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/bar.jade
new file mode 100644
index 0000000..24e3cee
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/bar.jade
@@ -0,0 +1 @@
+block bing
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/bing.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/bing.jade
new file mode 100644
index 0000000..9013b36
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/bing.jade
@@ -0,0 +1 @@
+.bing bong
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/expected.json
new file mode 100644
index 0000000..134aecf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/expected.json
@@ -0,0 +1,95 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "bar.jade",
+        "line": 1,
+        "filename": "<dirname>/foo.jade",
+        "fullPath": "<dirname>/bar.jade",
+        "str": "block bing\n",
+        "ast": {
+          "type": "Block",
+          "nodes": [
+            {
+              "type": "NamedBlock",
+              "nodes": [],
+              "line": 1,
+              "filename": "<dirname>/bar.jade",
+              "name": "bing",
+              "mode": "replace"
+            }
+          ],
+          "line": 0,
+          "filename": "<dirname>/bar.jade"
+        }
+      },
+      "line": 1,
+      "filename": "<dirname>/foo.jade"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Include",
+          "file": {
+            "type": "FileReference",
+            "path": "bing.jade",
+            "line": 4,
+            "filename": "<dirname>/foo.jade",
+            "fullPath": "<dirname>/bing.jade",
+            "str": ".bing bong\n",
+            "ast": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "bong",
+                        "line": 1,
+                        "filename": "<dirname>/bing.jade"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "class",
+                      "val": "'bing'",
+                      "escaped": false
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 1,
+                  "filename": "<dirname>/bing.jade"
+                }
+              ],
+              "line": 0,
+              "filename": "<dirname>/bing.jade"
+            }
+          },
+          "attrs": [],
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "line": 4,
+          "filename": "<dirname>/foo.jade"
+        }
+      ],
+      "line": 3,
+      "filename": "<dirname>/foo.jade",
+      "name": "bing",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "<dirname>/foo.jade"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/foo.jade b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/foo.jade
new file mode 100644
index 0000000..3dcf52a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/foo.jade
@@ -0,0 +1,4 @@
+extends bar.jade
+
+block bing
+  include bing.jade
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/index.js
new file mode 100644
index 0000000..6449515
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-load/test/index.js
@@ -0,0 +1,26 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var assert = require('assert');
+var walk = require('jade-walk');
+var lex = require('jade-lexer');
+var parse = require('jade-parser');
+var load = require('../');
+
+var filename = __dirname + '/foo.jade';
+var ast = load.file(filename, {
+  lex: lex,
+  parse: parse
+});
+
+ast = walk(ast, function (node) {
+  if (node.filename) node.filename = '<dirname>/' + path.basename(node.filename);
+  if (node.fullPath) node.fullPath = '<dirname>/' + path.basename(node.fullPath);
+}, {includeDependencies: true});
+
+var expected = JSON.parse(fs.readFileSync(__dirname + '/expected.json', 'utf8'));
+fs.writeFileSync(__dirname + '/output.json', JSON.stringify(ast, null, '  '));
+
+assert.deepEqual(expected, ast);
+console.log('tests passed');
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/.npmignore
new file mode 100644
index 0000000..cef0d7a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/.npmignore
@@ -0,0 +1,15 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
+coverage
+*.actual.json
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/LICENSE
new file mode 100644
index 0000000..27cc9f3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/README.md
new file mode 100644
index 0000000..9a4a625
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/README.md
@@ -0,0 +1,15 @@
+# jade-parser
+
+The jade parser (takes an array of tokens and converts it to an abstract syntax tree)
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-parser/master.svg)](https://travis-ci.org/jadejs/jade-parser)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-parser.svg)](https://gemnasium.com/jadejs/jade-parser)
+[![NPM version](https://img.shields.io/npm/v/jade-parser.svg)](https://www.npmjs.org/package/jade-parser)
+
+## Installation
+
+    npm install jade-parser
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/index.js
new file mode 100644
index 0000000..115268d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/index.js
@@ -0,0 +1,873 @@
+'use strict';
+
+var path = require('path');
+var TokenStream = require('token-stream');
+var inlineTags = require('./lib/inline-tags');
+
+var extname = path.extname;
+
+module.exports = parse;
+module.exports.Parser = Parser;
+function parse(tokens, filename) {
+  var parser = new Parser(tokens, filename);
+  var ast = parser.parse();
+  return JSON.parse(JSON.stringify(ast));
+};
+
+/**
+ * Initialize `Parser` with the given input `str` and `filename`.
+ *
+ * @param {String} str
+ * @param {String} filename
+ * @param {Object} options
+ * @api public
+ */
+
+function Parser(tokens, filename){
+  this.tokens = new TokenStream(tokens);
+  this.filename = filename;
+  this.inMixin = 0;
+};
+
+/**
+ * Parser prototype.
+ */
+
+Parser.prototype = {
+
+  /**
+   * Save original constructor
+   */
+
+  constructor: Parser,
+
+  error: function (message, code, token) {
+    var err = new Error(message + ' on line ' + token.line + ' of ' + token.filename);
+    err.code = 'JADE:' + code;
+    err.msg = message;
+    err.line = token.line;
+    err.filename = this.filename;
+    throw err;
+  },
+
+  /**
+   * Return the next token object.
+   *
+   * @return {Object}
+   * @api private
+   */
+
+  advance: function(){
+    return this.tokens.advance();
+  },
+
+  /**
+   * Single token lookahead.
+   *
+   * @return {Object}
+   * @api private
+   */
+
+  peek: function() {
+    return this.tokens.peek();
+  },
+
+  /**
+   * `n` token lookahead.
+   *
+   * @param {Number} n
+   * @return {Object}
+   * @api private
+   */
+
+  lookahead: function(n){
+    return this.tokens.lookahead(n);
+  },
+
+  /**
+   * Parse input returning a string of js for evaluation.
+   *
+   * @return {String}
+   * @api public
+   */
+
+  parse: function(){
+    var block ={type: 'Block', nodes: [], line: 0, filename: this.filename};
+
+    while ('eos' != this.peek().type) {
+      if ('newline' == this.peek().type) {
+        this.advance();
+      } else if ('text-html' == this.peek().type) {
+        block.nodes = block.nodes.concat(this.parseTextHtml());
+      } else {
+        var next = this.peek();
+        var expr = this.parseExpr();
+        block.nodes.push(expr);
+      }
+    }
+
+    return block;
+  },
+
+  /**
+   * Expect the given type, or throw an exception.
+   *
+   * @param {String} type
+   * @api private
+   */
+
+  expect: function(type){
+    if (this.peek().type === type) {
+      return this.advance();
+    } else {
+      this.error('expected "' + type + '", but got "' + this.peek().type + '"', 'INVALID_TOKEN', this.peek());
+    }
+  },
+
+  /**
+   * Accept the given `type`.
+   *
+   * @param {String} type
+   * @api private
+   */
+
+  accept: function(type){
+    if (this.peek().type === type) {
+      return this.advance();
+    }
+  },
+
+  /**
+   *   tag
+   * | doctype
+   * | mixin
+   * | include
+   * | filter
+   * | comment
+   * | text
+   * | each
+   * | code
+   * | yield
+   * | id
+   * | class
+   * | interpolation
+   */
+
+  parseExpr: function(){
+    switch (this.peek().type) {
+      case 'tag':
+        return this.parseTag();
+      case 'mixin':
+        return this.parseMixin();
+      case 'block':
+        return this.parseBlock();
+      case 'mixin-block':
+        return this.parseMixinBlock();
+      case 'case':
+        return this.parseCase();
+      case 'extends':
+        return this.parseExtends();
+      case 'include':
+        return this.parseInclude();
+      case 'doctype':
+        return this.parseDoctype();
+      case 'filter':
+        return this.parseFilter();
+      case 'comment':
+        return this.parseComment();
+      case 'text':
+      case 'start-jade-interpolation':
+        return this.parseText({block: true});
+      case 'each':
+        return this.parseEach();
+      case 'code':
+        return this.parseCode();
+      case 'blockcode':
+        return this.parseBlockCode();
+      case 'call':
+        return this.parseCall();
+      case 'interpolation':
+        return this.parseInterpolation();
+      case 'yield':
+        var block = {
+          type: 'Block',
+          nodes: [],
+          line: this.expect('yield').line,
+          filename: this.filename
+        };
+        block.yield = true;
+        return block;
+      case 'id':
+      case 'class':
+        this.tokens.defer({
+          type: 'tag',
+          val: 'div',
+          line: this.peek().line,
+          filename: this.filename
+        });
+        return this.parseExpr();
+      default:
+        this.error('unexpected token "' + this.peek().type + '"', 'INVALID_TOKEN', this.peek());
+    }
+  },
+
+  /**
+   * Text
+   */
+
+  parseText: function(options){
+    var tags = [];
+    var lineno = this.peek().line;
+    while(this.peek().type === 'text' || this.peek().type === 'start-jade-interpolation' || (options && options.block && this.peek().type === 'newline')) {
+      if (this.peek().type === 'text') {
+        var tok = this.advance();
+        tags.push({
+          type: 'Text',
+          val: tok.val,
+          line: tok.line,
+          filename: this.filename
+        });
+      } else if (this.peek().type === 'newline') {
+        var tok = this.expect('newline');
+        if (this.peek().type === 'text') {
+          tags.push({
+            type: 'Text',
+            val: '\n',
+            line: tok.line,
+            filename: this.filename
+          });
+        }
+      } else {
+        this.expect('start-jade-interpolation');
+        tags.push(this.parseExpr());
+        this.expect('end-jade-interpolation');
+      }
+    }
+    if (tags.length === 1) return tags[0];
+    else return {type: 'Block', nodes: tags, line: lineno, filename: this.filename};
+  },
+
+  parseTextHtml: function () {
+    var nodes = [];
+    var currentNode = null;
+    while (this.peek().type === 'text-html') {
+      var text = this.advance();
+      if (!currentNode) {
+        currentNode = {
+          type: 'Text',
+          val: text.val,
+          filename: this.filename,
+          line: text.line,
+          isHtml: true
+        };
+        nodes.push(currentNode);
+      } else {
+        currentNode.val += '\n' + text.val;
+      }
+      if (this.peek().type === 'indent') {
+        var block = this.block();
+        block.nodes.forEach(function (node) {
+          if (node.isHtml) {
+            if (!currentNode) {
+              currentNode = node;
+              nodes.push(currentNode);
+            } else {
+              currentNode.val += '\n' + node.val;
+            }
+          } else {
+            currentNode = null;
+            nodes.push(node);
+          }
+        });
+      } else if (this.peek().type === 'newline') {
+        this.advance();
+      }
+    }
+    return nodes;
+  },
+
+  /**
+   *   ':' expr
+   * | block
+   */
+
+  parseBlockExpansion: function(){
+    if (':' == this.peek().type) {
+      var tok = this.advance();
+      return {type: 'Block', nodes: [this.parseExpr()], line: tok.line, filename: this.filename};
+    } else {
+      return this.block();
+    }
+  },
+
+  /**
+   * case
+   */
+
+  parseCase: function(){
+    var tok = this.expect('case');
+    var node = {type: 'Case', expr: tok.val, line: tok.line, filename: this.filename};
+
+    var block = {type: 'Block', nodes: [], line: tok.line + 1, filename: this.filename};
+    block.filename = this.filename;
+    this.expect('indent');
+    while ('outdent' != this.peek().type) {
+      switch (this.peek().type) {
+        case 'comment':
+        case 'newline':
+          this.advance();
+          break;
+        case 'when':
+          block.nodes.push(this.parseWhen());
+          break;
+        case 'default':
+          block.nodes.push(this.parseDefault());
+          break;
+        default:
+          this.error('Unexpected token "' + this.peek().type
+                          + '", expected "when", "default" or "newline"', 'INVALID_TOKEN', this.peek());
+      }
+    }
+    this.expect('outdent');
+
+    node.block = block;
+
+    return node;
+  },
+
+  /**
+   * when
+   */
+
+  parseWhen: function(){
+    var tok = this.expect('when');
+    if (this.peek().type !== 'newline') {
+      return {
+        type: 'When',
+        expr: tok.val,
+        block: this.parseBlockExpansion(),
+        debug: false,
+        line: tok.line,
+        filename: this.filename
+      };
+    } else {
+      return {
+        type: 'When',
+        expr: tok.val,
+        debug: false,
+        line: tok.line,
+        filename: this.filename
+      };
+    }
+  },
+
+  /**
+   * default
+   */
+
+  parseDefault: function(){
+    var tok = this.expect('default');
+    return {
+      type: 'When',
+      expr: 'default',
+      block: this.parseBlockExpansion(),
+      debug: false,
+      line: tok.line,
+      filename: this.filename
+    };
+  },
+
+  /**
+   * code
+   */
+
+  parseCode: function(afterIf){
+    var tok = this.expect('code');
+    var node = {
+      type: 'Code',
+      val: tok.val,
+      buffer: tok.buffer,
+      escape: tok.escape,
+      line: tok.line,
+      filename: this.filename
+    };
+    // todo: why is this here?  It seems like a hacky workaround
+    if (node.val.match(/^ *else/)) node.debug = false;
+    var block;
+
+    // throw an error if an else does not have an if
+    if (tok.isElse && !tok.hasIf) {
+      this.error('Unexpected else without if', 'ELSE_NO_IF', tok);
+    }
+
+    // handle block
+    block = 'indent' == this.peek().type;
+    if (block) {
+      node.block = this.block();
+    }
+
+    // handle missing block
+    if (tok.requiresBlock && !block) {
+      node.block = {type: 'Block', nodes: [], line: tok.line, filename: this.filename};
+    }
+
+    // mark presense of if for future elses
+    if (tok.isIf && this.peek().isElse) {
+      this.peek().hasIf = true;
+    } else if (tok.isIf && this.peek().type === 'newline' && this.lookahead(1).isElse) {
+      this.lookahead(1).hasIf = true;
+    }
+
+    return node;
+  },
+  
+  /**
+   * block code
+   */
+
+  parseBlockCode: function(){
+    var line = this.expect('blockcode').line;
+    var node;
+    var body = this.peek();
+    var text = '';
+    if (body.type === 'start-pipeless-text') {
+      this.advance();
+      while (this.peek().type !== 'end-pipeless-text') {
+        var tok = this.advance();
+        switch (tok.type) {
+          case 'text':
+            text += tok.val;
+            break;
+          case 'newline':
+            text += '\n';
+            break;
+          default:
+            this.error('Unexpected token type: ' + tok.type, 'INVALID_TOKEN', tok);
+        }
+      }
+      this.advance();
+    }
+    return {
+      type: 'Code',
+      val: text,
+      buffer: false,
+      escape: false,
+      line: line,
+      filename: this.filename
+    };
+  },
+  /**
+   * comment
+   */
+
+  parseComment: function(){
+    var tok = this.expect('comment');
+    var block;
+    if (block = this.parseTextBlock()) {
+      return {
+        type: 'BlockComment',
+        val: tok.val,
+        block: block,
+        buffer: tok.buffer,
+        line: tok.line,
+        filename: this.filename
+      };
+    } else {
+      return {
+        type: 'Comment',
+        val: tok.val,
+        buffer: tok.buffer,
+        line: tok.line,
+        filename: this.filename
+      };
+    }
+  },
+
+  /**
+   * doctype
+   */
+
+  parseDoctype: function(){
+    var tok = this.expect('doctype');
+    return {
+      type: 'Doctype',
+      val: tok.val,
+      line: tok.line,
+      filename: this.filename
+    };
+  },
+
+  /**
+   * filter attrs? text-block
+   */
+
+  parseFilter: function(){
+    var tok = this.expect('filter');
+    var attrs = this.accept('attrs');
+    var block;
+
+    block = this.parseTextBlock() || {type: 'Block', nodes: [], line: tok.line, filename: this.filename};
+
+    return {type: 'Filter', name: tok.val, block: block, attrs: attrs ? attrs.attrs : [], line: tok.line, filename: this.filename};
+  },
+
+  /**
+   * each block
+   */
+
+  parseEach: function(){
+    var tok = this.expect('each');
+    var node = {
+      type: 'Each',
+      obj: tok.code,
+      val: tok.val,
+      key: tok.key,
+      block: this.block(),
+      line: tok.line,
+      filename: this.filename
+    };
+    if (this.peek().type == 'code' && this.peek().val == 'else') {
+      this.advance();
+      node.alternative = this.block();
+    }
+    return node;
+  },
+
+  /**
+   * 'extends' name
+   */
+
+  parseExtends: function(){
+    var tok = this.expect('extends');
+    return {
+      type: 'Extends',
+      file: {
+        type: 'FileReference',
+        path: tok.val.trim(),
+        line: tok.line,
+        filename: this.filename
+      },
+      line: tok.line,
+      filename: this.filename
+    };
+  },
+
+  /**
+   * 'block' name block
+   */
+
+  parseBlock: function(){
+    var tok = this.expect('block');
+
+    var node = 'indent' == this.peek().type ? this.block() : {type: 'Block', nodes: [], line: tok.line, filename: this.filename};
+    node.type = 'NamedBlock';
+    node.name = tok.val.trim();
+    node.mode = tok.mode;
+    node.line = tok.line;
+
+    return node;
+  },
+
+  parseMixinBlock: function () {
+    var tok = this.expect('mixin-block');
+    if (!this.inMixin) {
+      this.error('Anonymous blocks are not allowed unless they are part of a mixin.', 'BLOCK_OUTISDE_MIXIN', tok);
+    }
+    return {type: 'MixinBlock', line: tok.line, filename: this.filename};
+  },
+
+  /**
+   * include block?
+   */
+
+  parseInclude: function(){
+    var tok = this.expect('include');
+
+    return {
+      type: 'Include',
+      file: {
+        type: 'FileReference',
+        path: tok.val.trim(),
+        line: tok.line,
+        filename: this.filename
+      },
+      filter: tok.filter,
+      attrs: tok.attrs ? tok.attrs.attrs : [],
+      block: 'indent' == this.peek().type ? this.block() : {type: 'Block', nodes: []},
+      line: tok.line,
+      filename: this.filename
+    };
+  },
+
+  /**
+   * call ident block
+   */
+
+  parseCall: function(){
+    var tok = this.expect('call');
+    var name = tok.val;
+    var args = tok.args;
+    var mixin = {
+      type: 'Mixin',
+      name: name,
+      args: args,
+      block: {type: 'Block', nodes: [], line: tok.line, filename: this.filename},
+      call: true,
+      attrs: [],
+      attributeBlocks: [],
+      line: tok.line,
+      filename: this.filename
+    };
+
+    this.tag(mixin);
+    if (mixin.code) {
+      mixin.block.nodes.push(mixin.code);
+      delete mixin.code;
+    }
+    if (mixin.block.nodes.length === 0) mixin.block = null;
+    return mixin;
+  },
+
+  /**
+   * mixin block
+   */
+
+  parseMixin: function(){
+    var tok = this.expect('mixin');
+    var name = tok.val;
+    var args = tok.args;
+
+    // definition
+    if ('indent' == this.peek().type) {
+      this.inMixin++;
+      var mixin = {
+        type: 'Mixin',
+        name: name,
+        args: args,
+        block: this.block(),
+        call: false,
+        line: tok.line,
+        filename: this.filename
+      };
+      this.inMixin--;
+      return mixin;
+    // call
+    } else {
+      console.warn('Deprecated method of calling mixins, use `+name` syntax (' +
+                   this.filename + ' line ' + tok.line + ')');
+      return {
+        type: 'Mixin',
+        name: name,
+        args: args,
+        block: null,
+        call: true,
+        attrs: [],
+        attributeBlocks: [],
+        line: tok.line,
+        filename: this.filename
+      };
+    }
+  },
+
+  /**
+   * indent (text | newline)* outdent
+   */
+
+  parseTextBlock: function(){
+    var block = {type: 'Block', nodes: []};
+    if (this.peek().type !== 'start-pipeless-text') return;
+    this.advance();
+    while (this.peek().type !== 'end-pipeless-text') {
+      var tok = this.advance();
+      switch (tok.type) {
+        case 'text':
+          block.nodes.push({type: 'Text', val: tok.val, line: tok.line});
+          break;
+        case 'newline':
+          block.nodes.push({type: 'Text', val: '\n', line: tok.line});
+          break;
+        case 'start-jade-interpolation':
+          block.nodes.push(this.parseExpr());
+          this.expect('end-jade-interpolation');
+          break;
+        default:
+          this.error('Unexpected token type: ' + tok.type, 'INVALID_TOKEN', tok);
+      }
+    }
+    this.advance();
+    return block;
+  },
+
+  /**
+   * indent expr* outdent
+   */
+
+  block: function(){
+    var tok = this.expect('indent');
+    var block = {
+      type: 'Block',
+      nodes: [],
+      line: tok.line,
+      filename: this.filename
+    };
+    while ('outdent' != this.peek().type) {
+      if ('newline' == this.peek().type) {
+        this.advance();
+      } else if ('text-html' == this.peek().type) {
+        block.nodes = block.nodes.concat(this.parseTextHtml());
+      } else {
+        var expr = this.parseExpr();
+        block.nodes.push(expr);
+      }
+    }
+    this.expect('outdent');
+    return block;
+  },
+
+  /**
+   * interpolation (attrs | class | id)* (text | code | ':')? newline* block?
+   */
+
+  parseInterpolation: function(){
+    var tok = this.advance();
+    var tag = {
+      type: 'Tag',
+      name: tok.val,
+      selfClosing: tok.selfClosing,
+      block: {type: 'Block', nodes: []},
+      attrs: [],
+      attributeBlocks: [],
+      buffer: true, // indicates that this is an "interpolated" tag i.e. #{'tag-name'}
+      isInline: false,
+      line: tok.line,
+      filename: this.filename
+    };
+
+    return this.tag(tag);
+  },
+
+  /**
+   * tag (attrs | class | id)* (text | code | ':')? newline* block?
+   */
+
+  parseTag: function(){
+    var tok = this.advance();
+    var tag = {
+      type: 'Tag',
+      name: tok.val,
+      selfClosing: tok.selfClosing,
+      block: {type: 'Block', nodes: []},
+      attrs: [],
+      attributeBlocks: [],
+      isInline: inlineTags.indexOf(tok.val) !== -1,
+      line: tok.line,
+      filename: this.filename
+    };
+
+    return this.tag(tag);
+  },
+
+  /**
+   * Parse tag.
+   */
+
+  tag: function(tag){
+    var seenAttrs = false;
+    var attributeNames = [];
+    // (attrs | class | id)*
+    out:
+      while (true) {
+        switch (this.peek().type) {
+          case 'id':
+          case 'class':
+            var tok = this.advance();
+            if (tok.type === 'id') {
+              if (attributeNames.indexOf('id') !== -1) {
+                this.error('Duplicate attribute "id" is not allowed.', 'DUPLICATE_ID', tok);
+              }
+              attributeNames.push('id');
+            }
+            tag.attrs.push({
+              name: tok.type,
+              val: "'" + tok.val + "'",
+              escaped: false
+            });
+            continue;
+          case 'attrs':
+            if (seenAttrs) {
+              console.warn(this.filename + ', line ' + this.peek().line + ':\nYou should not have jade tags with multiple attributes.');
+            }
+            seenAttrs = true;
+            var tok = this.advance();
+            var attrs = tok.attrs;
+
+            if (tok.selfClosing) tag.selfClosing = true;
+
+            for (var i = 0; i < attrs.length; i++) {
+              if (attrs[i].name !== 'class') {
+                if (attributeNames.indexOf(attrs[i].name) !== -1) {
+                  this.error('Duplicate attribute "' + attrs[i].name + '" is not allowed.', 'DUPLICATE_ATTRIBUTE', tok);
+                }
+                attributeNames.push(attrs[i].name);
+              }
+              tag.attrs.push({
+                name: attrs[i].name,
+                val: attrs[i].val,
+                escaped: attrs[i].escaped
+              });
+            }
+            continue;
+          case '&attributes':
+            var tok = this.advance();
+            tag.attributeBlocks.push(tok.val);
+            break;
+          default:
+            break out;
+        }
+      }
+
+    // check immediate '.'
+    if ('dot' == this.peek().type) {
+      tag.textOnly = true;
+      this.advance();
+    }
+
+    // (text | code | ':')?
+    switch (this.peek().type) {
+      case 'text':
+        tag.block.nodes.push(this.parseText());
+        break;
+      case 'code':
+        tag.code = this.parseCode();
+        break;
+      case ':':
+        this.advance();
+        tag.block = {type: 'Block', nodes: [this.parseExpr()]};
+        break;
+      case 'newline':
+      case 'indent':
+      case 'outdent':
+      case 'eos':
+      case 'start-pipeless-text':
+      case 'end-jade-interpolation':
+        break;
+      default:
+        this.error('Unexpected token `' + this.peek().type + '` expected `text`, `code`, `:`, `newline` or `eos`', 'INVALID_TOKEN', this.peek())
+    }
+
+    // newline*
+    while ('newline' == this.peek().type) this.advance();
+
+    // block?
+    if (tag.textOnly) {
+      tag.block = this.parseTextBlock() || {type: 'Block', nodes: []};
+    } else if ('indent' == this.peek().type) {
+      var block = this.block();
+      for (var i = 0, len = block.nodes.length; i < len; ++i) {
+        tag.block.nodes.push(block.nodes[i]);
+      }
+    }
+
+    return tag;
+  }
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/lib/inline-tags.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/lib/inline-tags.js
new file mode 100644
index 0000000..a2345b1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/lib/inline-tags.js
@@ -0,0 +1,23 @@
+'use strict';
+
+module.exports = [
+    'a'
+  , 'abbr'
+  , 'acronym'
+  , 'b'
+  , 'br'
+  , 'code'
+  , 'em'
+  , 'font'
+  , 'i'
+  , 'img'
+  , 'ins'
+  , 'kbd'
+  , 'map'
+  , 'samp'
+  , 'small'
+  , 'span'
+  , 'strong'
+  , 'sub'
+  , 'sup'
+];
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/.npmignore
new file mode 100644
index 0000000..0ced68c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/.npmignore
@@ -0,0 +1,14 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
+coverage
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/.travis.yml
new file mode 100644
index 0000000..6e5919d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "0.10"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/LICENSE
new file mode 100644
index 0000000..27cc9f3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/README.md
new file mode 100644
index 0000000..0e9c12c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/README.md
@@ -0,0 +1,68 @@
+# token-stream
+
+Take an array of token and produce a more useful API to give to a parser.
+
+[![Build Status](https://img.shields.io/travis/jadejs/token-stream/master.svg)](https://travis-ci.org/jadejs/token-stream)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/token-stream.svg)](https://gemnasium.com/jadejs/token-stream)
+[![NPM version](https://img.shields.io/npm/v/token-stream.svg)](https://www.npmjs.org/package/token-stream)
+
+## Installation
+
+    npm install token-stream
+
+## Usage
+
+```js
+var TokenStream = require('token-stream');
+
+var stream = new TokenStream([
+  'a',
+  'b',
+  'c',
+  'd'
+]);
+assert(stream.peek() === 'a');
+assert(stream.lookahead(0) == 'a');
+assert(stream.lookahead(1) == 'b');
+
+assert(stream.advance() === 'a');
+assert(stream.peek() === 'b');
+assert(stream.lookahead(0) == 'b');
+assert(stream.lookahead(1) == 'c');
+
+stream.defer('z');
+assert(stream.peek() === 'z');
+assert(stream.lookahead(0) == 'z');
+assert(stream.lookahead(1) == 'b');
+assert(stream.advance() === 'z');
+assert(stream.advance() === 'b');
+assert(stream.advance() === 'c');
+assert(stream.advance() === 'd');
+
+// an error is thrown if you try and advance beyond the end of the stream
+assert.throws(function () {
+  stream.adavance();
+});
+```
+
+## API
+
+### stream.peek()
+
+Gets and returns the next item in the stream without advancing the stream's position.
+
+### stream.advance()
+
+Returns the next item in the stream and advances the stream by one item.
+
+### stream.defer(token)
+
+Put a token on the start of the stream (useful if you need to back track after calling advance.
+
+### stream.lookahead(index)
+
+Return the item at `index` position from the start of the stream, but don't advance the stream.  `stream.lookahead(0)` is equivalent to `stream.peek()`.
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/index.js
new file mode 100644
index 0000000..70641bb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/index.js
@@ -0,0 +1,30 @@
+'use strict';
+
+module.exports = TokenStream;
+function TokenStream(tokens) {
+  if (!Array.isArray(tokens)) {
+    throw new TypeError('tokens must be passed to TokenStream as an array.');
+  }
+  this._tokens = tokens;
+}
+TokenStream.prototype.lookahead = function (index) {
+  if (this._tokens.length <= index) {
+    throw new Error('Cannot read past the end of a stream');
+  }
+  return this._tokens[index];
+};
+TokenStream.prototype.peek = function () {
+  if (this._tokens.length === 0) {
+    throw new Error('Cannot read past the end of a stream');
+  }
+  return this._tokens[0];
+};
+TokenStream.prototype.advance = function () {
+  if (this._tokens.length === 0) {
+    throw new Error('Cannot read past the end of a stream');
+  }
+  return this._tokens.shift();
+};
+TokenStream.prototype.defer = function (token) {
+  this._tokens.unshift(token);
+};
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/package.json
new file mode 100644
index 0000000..9b5bd49
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/package.json
@@ -0,0 +1,46 @@
+{
+  "name": "token-stream",
+  "version": "0.0.1",
+  "description": "Take an array of token and produce a more useful API to give to a parser",
+  "dependencies": {},
+  "devDependencies": {
+    "istanbul": "^0.3.2"
+  },
+  "scripts": {
+    "test": "node test && npm run coverage",
+    "coverage": "istanbul cover test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/token-stream.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "f41daf0266e179dd3570f1256ad683ce9cf6a235",
+  "bugs": {
+    "url": "https://github.com/jadejs/token-stream/issues"
+  },
+  "homepage": "https://github.com/jadejs/token-stream",
+  "_id": "token-stream@0.0.1",
+  "_shasum": "ceeefc717a76c4316f126d0b9dbaa55d7e7df01a",
+  "_from": "token-stream@0.0.1",
+  "_npmVersion": "1.5.0-alpha-4",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "ceeefc717a76c4316f126d0b9dbaa55d7e7df01a",
+    "tarball": "http://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/test/index.js
new file mode 100644
index 0000000..8a127a2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/node_modules/token-stream/test/index.js
@@ -0,0 +1,46 @@
+'use strict';
+
+var assert = require('assert');
+var TokenStream = require('../');
+
+assert.throws(function () {
+  new TokenStream('foo,bar');
+});
+var stream = new TokenStream([
+  'a',
+  'b',
+  'c',
+  'd'
+]);
+assert.throws(function () {
+  stream.lookahead(9);
+});
+assert(stream.peek() === 'a');
+assert(stream.lookahead(0) == 'a');
+assert(stream.lookahead(1) == 'b');
+
+assert(stream.advance() === 'a');
+assert(stream.peek() === 'b');
+assert(stream.lookahead(0) == 'b');
+assert(stream.lookahead(1) == 'c');
+
+stream.defer('z');
+assert(stream.peek() === 'z');
+assert(stream.lookahead(0) == 'z');
+assert(stream.lookahead(1) == 'b');
+assert(stream.advance() === 'z');
+assert(stream.advance() === 'b');
+assert(stream.advance() === 'c');
+assert(stream.advance() === 'd');
+assert.throws(function () {
+  stream.peek();
+});
+assert.throws(function () {
+  stream.lookahead(0);
+});
+assert.throws(function () {
+  stream.lookahead(1);
+});
+assert.throws(function () {
+  stream.advance();
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/package.json
new file mode 100644
index 0000000..181e928
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/package.json
@@ -0,0 +1,52 @@
+{
+  "name": "jade-parser",
+  "version": "0.0.8",
+  "description": "The jade parser (takes an array of tokens and converts it to an abstract syntax tree)",
+  "keywords": [],
+  "dependencies": {
+    "token-stream": "0.0.1"
+  },
+  "devDependencies": {
+    "get-repo": "^1.0.0",
+    "istanbul": "^0.3.4"
+  },
+  "scripts": {
+    "update-test-cases": "node test/update-test-cases",
+    "test": "node test && npm run coverage",
+    "coverage": "istanbul cover test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-parser.git"
+  },
+  "author": {
+    "name": "ForbesLindesay"
+  },
+  "license": "MIT",
+  "gitHead": "62bac961c75b973f5c32af33a63ffe19bc8a3722",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-parser/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-parser",
+  "_id": "jade-parser@0.0.8",
+  "_shasum": "ee4bcbc9b1ce2fda6211a06dad7ccc3d092caa05",
+  "_from": "jade-parser@*",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindeay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "ee4bcbc9b1ce2fda6211a06dad7ccc3d092caa05",
+    "tarball": "http://registry.npmjs.org/jade-parser/-/jade-parser-0.0.8.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-parser/-/jade-parser-0.0.8.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs-data.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs-data.expected.json
new file mode 100644
index 0000000..79958c8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs-data.expected.json
@@ -0,0 +1,95 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var user = { name: 'tobi' }",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "attrs-data.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "data-user",
+          "val": "user",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "attrs-data.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "data-items",
+          "val": "[1,2,3]",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "attrs-data.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "data-username",
+          "val": "'tobi'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "attrs-data.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "data-escaped",
+          "val": "{message: \"Let's rock!\"}",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "attrs-data.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "attrs-data.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs-data.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs-data.tokens.json
new file mode 100644
index 0000000..cc05f84
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs-data.tokens.json
@@ -0,0 +1,15 @@
+{"type":"code","line":1,"val":"var user = { name: 'tobi' }","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"foo","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"data-user","val":"user","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"data-items","val":"[1,2,3]","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"foo","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"data-username","val":"'tobi'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"foo","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"data-escaped","val":"{message: \"Let's rock!\"}","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.expected.json
new file mode 100644
index 0000000..330a830
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.expected.json
@@ -0,0 +1,567 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "contact",
+            "line": 1,
+            "filename": "attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/contact'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 1,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "save",
+            "line": 2,
+            "filename": "attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/save'",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'button'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 2,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": true,
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": true,
+          "escaped": false
+        },
+        {
+          "name": "baz",
+          "val": true,
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 3,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "'foo, bar, baz'",
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": "1",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 4,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "'((foo))'",
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": "(1) ? 1 : 0",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 5,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "select",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "option",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Foo",
+                  "line": 7,
+                  "filename": "attrs.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "value",
+                "val": "'foo'",
+                "escaped": true
+              },
+              {
+                "name": "selected",
+                "val": true,
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 7,
+            "filename": "attrs.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "option",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Bar",
+                  "line": 8,
+                  "filename": "attrs.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "selected",
+                "val": true,
+                "escaped": true
+              },
+              {
+                "name": "value",
+                "val": "'bar'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "\"class:\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 9,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "input",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "pattern",
+          "val": "'\\\\S+'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 10,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "contact",
+            "line": 12,
+            "filename": "attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/contact'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 12,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "save",
+            "line": 13,
+            "filename": "attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/save'",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'button'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 13,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": true,
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": true,
+          "escaped": false
+        },
+        {
+          "name": "baz",
+          "val": true,
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 14,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "'foo, bar, baz'",
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": "1",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 15,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "'((foo))'",
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": "(1) ? 1 : 0",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 16,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "select",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "option",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Foo",
+                  "line": 18,
+                  "filename": "attrs.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "value",
+                "val": "'foo'",
+                "escaped": true
+              },
+              {
+                "name": "selected",
+                "val": true,
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 18,
+            "filename": "attrs.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "option",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Bar",
+                  "line": 19,
+                  "filename": "attrs.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "selected",
+                "val": true,
+                "escaped": true
+              },
+              {
+                "name": "value",
+                "val": "'bar'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 19,
+            "filename": "attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 17,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "\"class:\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 20,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "input",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "pattern",
+          "val": "'\\\\S+'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 21,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "terse",
+          "val": "\"true\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 22,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var attrs = {foo: 'bar', bar: '<baz>'}",
+      "buffer": false,
+      "escape": false,
+      "line": 24,
+      "filename": "attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [
+        "attrs"
+      ],
+      "isInline": false,
+      "line": 26,
+      "filename": "attrs.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "attrs.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.interpolation.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.interpolation.expected.json
new file mode 100644
index 0000000..e2e1c61
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.interpolation.expected.json
@@ -0,0 +1,55 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var id = 5",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "attrs.interpolation.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/user/' + (id) + ''",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 2,
+      "filename": "attrs.interpolation.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "bar",
+          "val": "'stuff \\#{here} yup'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "attrs.interpolation.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "attrs.interpolation.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.interpolation.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.interpolation.tokens.json
new file mode 100644
index 0000000..a7ccb4b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.interpolation.tokens.json
@@ -0,0 +1,8 @@
+{"type":"code","line":1,"val":"var id = 5","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'/user/' + (id) + ''","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"bar","val":"'stuff \\#{here} yup'","escaped":true}]}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.js.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.js.expected.json
new file mode 100644
index 0000000..3c4cd85
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.js.expected.json
@@ -0,0 +1,320 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var id = 5",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "function answer() { return 42; }",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/user/' + id",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'button'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 3,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/user/' + id",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'button'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 4,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "meta",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "key",
+          "val": "'answer'",
+          "escaped": true
+        },
+        {
+          "name": "value",
+          "val": "answer()",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "['class1', 'class2']",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 6,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'tag-class'",
+          "escaped": false
+        },
+        {
+          "name": "class",
+          "val": "['class1', 'class2']",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 7,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/user/' + id",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'button'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 9,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "href",
+          "val": "'/user/' + id",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'button'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 10,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "meta",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "key",
+          "val": "'answer'",
+          "escaped": true
+        },
+        {
+          "name": "value",
+          "val": "answer()",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 11,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "['class1', 'class2']",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 12,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'tag-class'",
+          "escaped": false
+        },
+        {
+          "name": "class",
+          "val": "['class1', 'class2']",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 13,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "id",
+          "val": "id",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [
+        "{foo: 'bar'}"
+      ],
+      "isInline": false,
+      "line": 15,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var bar = null",
+      "buffer": false,
+      "escape": false,
+      "line": 16,
+      "filename": "attrs.js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "null",
+          "escaped": true
+        },
+        {
+          "name": "bar",
+          "val": "bar",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [
+        "{baz: 'baz'}"
+      ],
+      "isInline": false,
+      "line": 17,
+      "filename": "attrs.js.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "attrs.js.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.js.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.js.tokens.json
new file mode 100644
index 0000000..48eefb3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.js.tokens.json
@@ -0,0 +1,47 @@
+{"type":"code","line":1,"val":"var id = 5","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"function answer() { return 42; }","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"a","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"meta","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"key","val":"'answer'","escaped":true},{"name":"value","val":"answer()","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"a","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"a","selfClosing":false}
+{"type":"class","line":7,"val":"tag-class"}
+{"type":"attrs","line":7,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"a","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"a","selfClosing":false}
+{"type":"attrs","line":10,"attrs":[{"name":"href","val":"'/user/' + id","escaped":true},{"name":"class","val":"'button'","escaped":true}]}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"meta","selfClosing":false}
+{"type":"attrs","line":11,"attrs":[{"name":"key","val":"'answer'","escaped":true},{"name":"value","val":"answer()","escaped":true}]}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"a","selfClosing":false}
+{"type":"attrs","line":12,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"a","selfClosing":false}
+{"type":"class","line":13,"val":"tag-class"}
+{"type":"attrs","line":13,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"div","selfClosing":false}
+{"type":"attrs","line":15,"attrs":[{"name":"id","val":"id","escaped":true}]}
+{"type":"&attributes","line":15,"val":"{foo: 'bar'}"}
+{"type":"newline","line":16}
+{"type":"code","line":16,"val":"var bar = null","escape":false,"buffer":false}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"div","selfClosing":false}
+{"type":"attrs","line":17,"attrs":[{"name":"foo","val":"null","escaped":true},{"name":"bar","val":"bar","escaped":true}]}
+{"type":"&attributes","line":17,"val":"{baz: 'baz'}"}
+{"type":"newline","line":18}
+{"type":"eos","line":18}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.tokens.json
new file mode 100644
index 0000000..39050d7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.tokens.json
@@ -0,0 +1,77 @@
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"href","val":"'/contact'","escaped":true}]}
+{"type":"text","line":1,"val":"contact"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'/save'","escaped":true}]}
+{"type":"class","line":2,"val":"button"}
+{"type":"text","line":2,"val":"save"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"foo","val":true,"escaped":true},{"name":"bar","val":true,"escaped":false},{"name":"baz","val":true,"escaped":false}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"a","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"foo","val":"'foo, bar, baz'","escaped":true},{"name":"bar","val":"1","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"a","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"foo","val":"'((foo))'","escaped":true},{"name":"bar","val":"(1) ? 1 : 0","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"select","selfClosing":false}
+{"type":"indent","line":7,"val":2}
+{"type":"tag","line":7,"val":"option","selfClosing":false}
+{"type":"attrs","line":7,"attrs":[{"name":"value","val":"'foo'","escaped":true},{"name":"selected","val":true,"escaped":false}]}
+{"type":"text","line":7,"val":"Foo"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"option","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"selected","val":true,"escaped":true},{"name":"value","val":"'bar'","escaped":true}]}
+{"type":"text","line":8,"val":"Bar"}
+{"type":"outdent","line":9}
+{"type":"tag","line":9,"val":"a","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"foo","val":"\"class:\"","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"input","selfClosing":false}
+{"type":"attrs","line":10,"attrs":[{"name":"pattern","val":"'\\\\S+'","escaped":true}]}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"a","selfClosing":false}
+{"type":"attrs","line":12,"attrs":[{"name":"href","val":"'/contact'","escaped":true}]}
+{"type":"text","line":12,"val":"contact"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"a","selfClosing":false}
+{"type":"attrs","line":13,"attrs":[{"name":"href","val":"'/save'","escaped":true}]}
+{"type":"class","line":13,"val":"button"}
+{"type":"text","line":13,"val":"save"}
+{"type":"newline","line":14}
+{"type":"tag","line":14,"val":"a","selfClosing":false}
+{"type":"attrs","line":14,"attrs":[{"name":"foo","val":true,"escaped":true},{"name":"bar","val":true,"escaped":false},{"name":"baz","val":true,"escaped":false}]}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"a","selfClosing":false}
+{"type":"attrs","line":15,"attrs":[{"name":"foo","val":"'foo, bar, baz'","escaped":true},{"name":"bar","val":"1","escaped":true}]}
+{"type":"newline","line":16}
+{"type":"tag","line":16,"val":"a","selfClosing":false}
+{"type":"attrs","line":16,"attrs":[{"name":"foo","val":"'((foo))'","escaped":true},{"name":"bar","val":"(1) ? 1 : 0","escaped":true}]}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"select","selfClosing":false}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"option","selfClosing":false}
+{"type":"attrs","line":18,"attrs":[{"name":"value","val":"'foo'","escaped":true},{"name":"selected","val":true,"escaped":false}]}
+{"type":"text","line":18,"val":"Foo"}
+{"type":"newline","line":19}
+{"type":"tag","line":19,"val":"option","selfClosing":false}
+{"type":"attrs","line":19,"attrs":[{"name":"selected","val":true,"escaped":true},{"name":"value","val":"'bar'","escaped":true}]}
+{"type":"text","line":19,"val":"Bar"}
+{"type":"outdent","line":20}
+{"type":"tag","line":20,"val":"a","selfClosing":false}
+{"type":"attrs","line":20,"attrs":[{"name":"foo","val":"\"class:\"","escaped":true}]}
+{"type":"newline","line":21}
+{"type":"tag","line":21,"val":"input","selfClosing":false}
+{"type":"attrs","line":21,"attrs":[{"name":"pattern","val":"'\\\\S+'","escaped":true}]}
+{"type":"newline","line":22}
+{"type":"tag","line":22,"val":"foo","selfClosing":false}
+{"type":"attrs","line":22,"attrs":[{"name":"terse","val":"\"true\"","escaped":true}]}
+{"type":"newline","line":24}
+{"type":"code","line":24,"val":"var attrs = {foo: 'bar', bar: '<baz>'}","escape":false,"buffer":false}
+{"type":"newline","line":26}
+{"type":"tag","line":26,"val":"div","selfClosing":false}
+{"type":"&attributes","line":26,"val":"attrs"}
+{"type":"newline","line":27}
+{"type":"eos","line":27}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.unescaped.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.unescaped.expected.json
new file mode 100644
index 0000000..9a086fa
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.unescaped.expected.json
@@ -0,0 +1,70 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "<%= user.title %>",
+                        "line": 3,
+                        "filename": "attrs.unescaped.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "attrs.unescaped.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'user-<%= user.id %>'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "attrs.unescaped.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'text/x-template'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "attrs.unescaped.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "attrs.unescaped.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.unescaped.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.unescaped.tokens.json
new file mode 100644
index 0000000..1ef6841
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/attrs.unescaped.tokens.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"div","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"id","val":"'user-<%= user.id %>'","escaped":false}]}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"text","line":3,"val":"<%= user.title %>"}
+{"type":"outdent","line":3}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/basic.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/basic.expected.json
new file mode 100644
index 0000000..1560775
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/basic.expected.json
@@ -0,0 +1,58 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "Title",
+                        "line": 3,
+                        "filename": "basic.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "basic.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "basic.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "basic.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "basic.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/basic.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/basic.tokens.json
new file mode 100644
index 0000000..9fcab14
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/basic.tokens.json
@@ -0,0 +1,9 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"text","line":3,"val":"Title"}
+{"type":"outdent","line":3}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blanks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blanks.expected.json
new file mode 100644
index 0000000..7a0fb33
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blanks.expected.json
@@ -0,0 +1,85 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 4,
+                  "filename": "blanks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "blanks.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 6,
+                  "filename": "blanks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 6,
+            "filename": "blanks.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "baz",
+                  "line": 8,
+                  "filename": "blanks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "blanks.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "blanks.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "blanks.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blanks.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blanks.tokens.json
new file mode 100644
index 0000000..c685c8d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blanks.tokens.json
@@ -0,0 +1,13 @@
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"li","selfClosing":false}
+{"type":"text","line":4,"val":"foo"}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"text","line":6,"val":"bar"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"li","selfClosing":false}
+{"type":"text","line":8,"val":"baz"}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-code.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-code.expected.json
new file mode 100644
index 0000000..c7ca834
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-code.expected.json
@@ -0,0 +1,75 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "list = [\"uno\", \"dos\", \"tres\",\n        \"cuatro\", \"cinco\", \"seis\"];",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "block-code.tokens.json"
+    },
+    {
+      "type": "Comment",
+      "val": " Without a block, the element is accepted and no code is generated",
+      "buffer": false,
+      "line": 4,
+      "filename": "block-code.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "",
+      "buffer": false,
+      "escape": false,
+      "line": 5,
+      "filename": "block-code.tokens.json"
+    },
+    {
+      "type": "Each",
+      "obj": "list",
+      "val": "item",
+      "key": "$index",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Code",
+            "val": "string = item.charAt(0)\n\n  .toUpperCase() +\nitem.slice(1);",
+            "buffer": false,
+            "escape": false,
+            "line": 7,
+            "filename": "block-code.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 12,
+            "filename": "block-code.tokens.json",
+            "code": {
+              "type": "Code",
+              "val": "string",
+              "buffer": true,
+              "escape": true,
+              "line": 12,
+              "filename": "block-code.tokens.json"
+            }
+          }
+        ],
+        "line": 7,
+        "filename": "block-code.tokens.json"
+      },
+      "line": 6,
+      "filename": "block-code.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "block-code.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-code.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-code.tokens.json
new file mode 100644
index 0000000..3076fa1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-code.tokens.json
@@ -0,0 +1,28 @@
+{"type":"blockcode","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"list = [\"uno\", \"dos\", \"tres\","}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"        \"cuatro\", \"cinco\", \"seis\"];"}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"comment","line":4,"val":" Without a block, the element is accepted and no code is generated","buffer":false}
+{"type":"newline","line":5}
+{"type":"blockcode","line":5}
+{"type":"newline","line":6}
+{"type":"each","line":6,"val":"item","key":"$index","code":"list"}
+{"type":"indent","line":7,"val":2}
+{"type":"blockcode","line":7}
+{"type":"start-pipeless-text","line":7}
+{"type":"text","line":8,"val":"string = item.charAt(0)"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":""}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":"  .toUpperCase() +"}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"item.slice(1);"}
+{"type":"end-pipeless-text","line":11}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"li","selfClosing":false}
+{"type":"code","line":12,"val":"string","escape":true,"buffer":true}
+{"type":"outdent","line":12}
+{"type":"eos","line":12}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.expected.json
new file mode 100644
index 0000000..ffa5d2f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.expected.json
@@ -0,0 +1,127 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "a",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "foo",
+                        "line": 2,
+                        "filename": "block-expansion.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "href",
+                      "val": "'#'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": true,
+                  "line": 2,
+                  "filename": "block-expansion.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "block-expansion.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "a",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "bar",
+                        "line": 3,
+                        "filename": "block-expansion.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "href",
+                      "val": "'#'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": true,
+                  "line": 3,
+                  "filename": "block-expansion.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "block-expansion.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "block-expansion.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "baz",
+            "line": 5,
+            "filename": "block-expansion.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "block-expansion.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "block-expansion.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.shorthands.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.shorthands.expected.json
new file mode 100644
index 0000000..05e0767
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.shorthands.expected.json
@@ -0,0 +1,89 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "div",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "baz",
+                              "line": 2,
+                              "filename": "block-expansion.shorthands.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [
+                          {
+                            "name": "id",
+                            "val": "'bar'",
+                            "escaped": false
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 2,
+                        "filename": "block-expansion.shorthands.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "class",
+                      "val": "'foo'",
+                      "escaped": false
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 2,
+                  "filename": "block-expansion.shorthands.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'list-item'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "block-expansion.shorthands.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "block-expansion.shorthands.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "block-expansion.shorthands.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.shorthands.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.shorthands.tokens.json
new file mode 100644
index 0000000..359ab89
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.shorthands.tokens.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"ul","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"li","selfClosing":false}
+{"type":"class","line":2,"val":"list-item"}
+{"type":":","line":2}
+{"type":"class","line":2,"val":"foo"}
+{"type":":","line":2}
+{"type":"id","line":2,"val":"bar"}
+{"type":"text","line":2,"val":"baz"}
+{"type":"outdent","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.tokens.json
new file mode 100644
index 0000000..0a00fc8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/block-expansion.tokens.json
@@ -0,0 +1,17 @@
+{"type":"tag","line":1,"val":"ul","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"li","selfClosing":false}
+{"type":":","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"href","val":"'#'","escaped":true}]}
+{"type":"text","line":2,"val":"foo"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"li","selfClosing":false}
+{"type":":","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"href","val":"'#'","escaped":true}]}
+{"type":"text","line":3,"val":"bar"}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"text","line":5,"val":"baz"}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blockquote.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blockquote.expected.json
new file mode 100644
index 0000000..5c12d00
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blockquote.expected.json
@@ -0,0 +1,64 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "figure",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "blockquote",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Try to define yourself by what you do, and you&#8217;ll burnout every time. You are. That is enough. I rest in that.",
+                  "line": 3,
+                  "filename": "blockquote.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "blockquote.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "figcaption",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "from @thefray at 1:43pm on May 10",
+                  "line": 4,
+                  "filename": "blockquote.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "blockquote.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "blockquote.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "blockquote.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blockquote.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blockquote.tokens.json
new file mode 100644
index 0000000..0632a2e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blockquote.tokens.json
@@ -0,0 +1,10 @@
+{"type":"tag","line":1,"val":"figure","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"blockquote","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"text","line":3,"val":"Try to define yourself by what you do, and you&#8217;ll burnout every time. You are. That is enough. I rest in that."}
+{"type":"outdent","line":4}
+{"type":"tag","line":4,"val":"figcaption","selfClosing":false}
+{"type":"text","line":4,"val":"from @thefray at 1:43pm on May 10"}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-blocks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-blocks.expected.json
new file mode 100644
index 0000000..bd10ee7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-blocks.expected.json
@@ -0,0 +1,48 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "./auxiliary/blocks-in-blocks-layout.jade",
+        "line": 1,
+        "filename": "blocks-in-blocks.tokens.json"
+      },
+      "line": 1,
+      "filename": "blocks-in-blocks.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h1",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Page 2",
+                "line": 4,
+                "filename": "blocks-in-blocks.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 4,
+          "filename": "blocks-in-blocks.tokens.json"
+        }
+      ],
+      "line": 3,
+      "filename": "blocks-in-blocks.tokens.json",
+      "name": "body",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "blocks-in-blocks.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-blocks.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-blocks.tokens.json
new file mode 100644
index 0000000..7a93d38
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-blocks.tokens.json
@@ -0,0 +1,8 @@
+{"type":"extends","line":1,"val":"./auxiliary/blocks-in-blocks-layout.jade"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"body","mode":"replace"}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"text","line":4,"val":"Page 2"}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-if.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-if.expected.json
new file mode 100644
index 0000000..1a78b46
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-if.expected.json
@@ -0,0 +1,225 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Comment",
+      "val": " see https://github.com/jadejs/jade/issues/1589",
+      "buffer": false,
+      "line": 1,
+      "filename": "blocks-in-if.tokens.json"
+    },
+    {
+      "type": "Text",
+      "val": "",
+      "line": 2,
+      "filename": "blocks-in-if.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var ajax = true",
+      "buffer": false,
+      "escape": false,
+      "line": 3,
+      "filename": "blocks-in-if.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "if( ajax )",
+      "buffer": false,
+      "escape": false,
+      "line": 5,
+      "filename": "blocks-in-if.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Comment",
+            "val": " return only contents if ajax requests",
+            "buffer": false,
+            "line": 6,
+            "filename": "blocks-in-if.tokens.json"
+          },
+          {
+            "type": "NamedBlock",
+            "nodes": [
+              {
+                "type": "Tag",
+                "name": "p",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": [
+                    {
+                      "type": "Text",
+                      "val": "ajax contents",
+                      "line": 8,
+                      "filename": "blocks-in-if.tokens.json"
+                    }
+                  ]
+                },
+                "attrs": [],
+                "attributeBlocks": [],
+                "isInline": false,
+                "line": 8,
+                "filename": "blocks-in-if.tokens.json"
+              }
+            ],
+            "line": 7,
+            "filename": "blocks-in-if.tokens.json",
+            "name": "contents",
+            "mode": "replace"
+          }
+        ],
+        "line": 6,
+        "filename": "blocks-in-if.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "else",
+      "buffer": false,
+      "escape": false,
+      "line": 10,
+      "filename": "blocks-in-if.tokens.json",
+      "debug": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Comment",
+            "val": " return all html",
+            "buffer": false,
+            "line": 11,
+            "filename": "blocks-in-if.tokens.json"
+          },
+          {
+            "type": "Doctype",
+            "val": "html",
+            "line": 12,
+            "filename": "blocks-in-if.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "html",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "head",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "meta",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "charset",
+                            "val": "'utf8'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 15,
+                        "filename": "blocks-in-if.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "title",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "sample",
+                              "line": 16,
+                              "filename": "blocks-in-if.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 16,
+                        "filename": "blocks-in-if.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "body",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "NamedBlock",
+                              "nodes": [
+                                {
+                                  "type": "Tag",
+                                  "name": "p",
+                                  "selfClosing": false,
+                                  "block": {
+                                    "type": "Block",
+                                    "nodes": [
+                                      {
+                                        "type": "Text",
+                                        "val": "all contetns",
+                                        "line": 19,
+                                        "filename": "blocks-in-if.tokens.json"
+                                      }
+                                    ]
+                                  },
+                                  "attrs": [],
+                                  "attributeBlocks": [],
+                                  "isInline": false,
+                                  "line": 19,
+                                  "filename": "blocks-in-if.tokens.json"
+                                }
+                              ],
+                              "line": 18,
+                              "filename": "blocks-in-if.tokens.json",
+                              "name": "contents",
+                              "mode": "replace"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 17,
+                        "filename": "blocks-in-if.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 14,
+                  "filename": "blocks-in-if.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 13,
+            "filename": "blocks-in-if.tokens.json"
+          }
+        ],
+        "line": 11,
+        "filename": "blocks-in-if.tokens.json"
+      }
+    }
+  ],
+  "line": 0,
+  "filename": "blocks-in-if.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-if.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-if.tokens.json
new file mode 100644
index 0000000..9a2e87e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/blocks-in-if.tokens.json
@@ -0,0 +1,43 @@
+{"type":"comment","line":1,"val":" see https://github.com/jadejs/jade/issues/1589","buffer":false}
+{"type":"text","line":2,"val":""}
+{"type":"newline","line":3}
+{"type":"code","line":3,"val":"var ajax = true","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"code","line":5,"val":"if( ajax )","escape":false,"buffer":false}
+{"type":"indent","line":6,"val":4}
+{"type":"comment","line":6,"val":" return only contents if ajax requests","buffer":false}
+{"type":"newline","line":7}
+{"type":"block","line":7,"val":"contents","mode":"replace"}
+{"type":"indent","line":8,"val":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"ajax contents"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"code","line":10,"val":"else","escape":false,"buffer":false}
+{"type":"indent","line":11,"val":4}
+{"type":"comment","line":11,"val":" return all html","buffer":false}
+{"type":"newline","line":12}
+{"type":"doctype","line":12,"val":"html"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"html","selfClosing":false}
+{"type":"indent","line":14,"val":8}
+{"type":"tag","line":14,"val":"head","selfClosing":false}
+{"type":"indent","line":15,"val":12}
+{"type":"tag","line":15,"val":"meta","selfClosing":false}
+{"type":"attrs","line":15,"attrs":[{"name":"charset","val":"'utf8'","escaped":true}]}
+{"type":"newline","line":16}
+{"type":"tag","line":16,"val":"title","selfClosing":false}
+{"type":"text","line":16,"val":"sample"}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"body","selfClosing":false}
+{"type":"indent","line":18,"val":16}
+{"type":"block","line":18,"val":"contents","mode":"replace"}
+{"type":"indent","line":19,"val":20}
+{"type":"tag","line":19,"val":"p","selfClosing":false}
+{"type":"text","line":19,"val":"all contetns"}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"eos","line":20}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case-blocks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case-blocks.expected.json
new file mode 100644
index 0000000..a41baed
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case-blocks.expected.json
@@ -0,0 +1,163 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Code",
+                  "val": "var friends = 1",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 3,
+                  "filename": "case-blocks.tokens.json"
+                },
+                {
+                  "type": "Case",
+                  "expr": "friends",
+                  "line": 4,
+                  "filename": "case-blocks.tokens.json",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "When",
+                        "expr": "0",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have no friends",
+                                    "line": 6,
+                                    "filename": "case-blocks.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 6,
+                              "filename": "case-blocks.tokens.json"
+                            }
+                          ],
+                          "line": 6,
+                          "filename": "case-blocks.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 5,
+                        "filename": "case-blocks.tokens.json"
+                      },
+                      {
+                        "type": "When",
+                        "expr": "1",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have a friend",
+                                    "line": 8,
+                                    "filename": "case-blocks.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 8,
+                              "filename": "case-blocks.tokens.json"
+                            }
+                          ],
+                          "line": 8,
+                          "filename": "case-blocks.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 7,
+                        "filename": "case-blocks.tokens.json"
+                      },
+                      {
+                        "type": "When",
+                        "expr": "default",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have #{friends} friends",
+                                    "line": 10,
+                                    "filename": "case-blocks.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 10,
+                              "filename": "case-blocks.tokens.json"
+                            }
+                          ],
+                          "line": 10,
+                          "filename": "case-blocks.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 9,
+                        "filename": "case-blocks.tokens.json"
+                      }
+                    ],
+                    "line": 5,
+                    "filename": "case-blocks.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "case-blocks.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "case-blocks.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "case-blocks.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case-blocks.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case-blocks.tokens.json
new file mode 100644
index 0000000..958cbf8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case-blocks.tokens.json
@@ -0,0 +1,27 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"code","line":3,"val":"var friends = 1","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"case","line":4,"val":"friends"}
+{"type":"indent","line":5,"val":6}
+{"type":"when","line":5,"val":"0"}
+{"type":"indent","line":6,"val":8}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"text","line":6,"val":"you have no friends"}
+{"type":"outdent","line":7}
+{"type":"when","line":7,"val":"1"}
+{"type":"indent","line":8,"val":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"you have a friend"}
+{"type":"outdent","line":9}
+{"type":"default","line":9}
+{"type":"indent","line":10,"val":8}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"you have #{friends} friends"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case.expected.json
new file mode 100644
index 0000000..9e4e0b9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case.expected.json
@@ -0,0 +1,261 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Code",
+                  "val": "var friends = 1",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 3,
+                  "filename": "case.tokens.json"
+                },
+                {
+                  "type": "Case",
+                  "expr": "friends",
+                  "line": 4,
+                  "filename": "case.tokens.json",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "When",
+                        "expr": "0",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have no friends",
+                                    "line": 5,
+                                    "filename": "case.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 5,
+                              "filename": "case.tokens.json"
+                            }
+                          ],
+                          "line": 5,
+                          "filename": "case.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 5,
+                        "filename": "case.tokens.json"
+                      },
+                      {
+                        "type": "When",
+                        "expr": "1",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have a friend",
+                                    "line": 6,
+                                    "filename": "case.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 6,
+                              "filename": "case.tokens.json"
+                            }
+                          ],
+                          "line": 6,
+                          "filename": "case.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 6,
+                        "filename": "case.tokens.json"
+                      },
+                      {
+                        "type": "When",
+                        "expr": "default",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have #{friends} friends",
+                                    "line": 7,
+                                    "filename": "case.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 7,
+                              "filename": "case.tokens.json"
+                            }
+                          ],
+                          "line": 7,
+                          "filename": "case.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 7,
+                        "filename": "case.tokens.json"
+                      }
+                    ],
+                    "line": 5,
+                    "filename": "case.tokens.json"
+                  }
+                },
+                {
+                  "type": "Code",
+                  "val": "var friends = 0",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 8,
+                  "filename": "case.tokens.json"
+                },
+                {
+                  "type": "Case",
+                  "expr": "friends",
+                  "line": 9,
+                  "filename": "case.tokens.json",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "When",
+                        "expr": "0",
+                        "debug": false,
+                        "line": 10,
+                        "filename": "case.tokens.json"
+                      },
+                      {
+                        "type": "When",
+                        "expr": "1",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have very few friends",
+                                    "line": 12,
+                                    "filename": "case.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 12,
+                              "filename": "case.tokens.json"
+                            }
+                          ],
+                          "line": 12,
+                          "filename": "case.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 11,
+                        "filename": "case.tokens.json"
+                      },
+                      {
+                        "type": "When",
+                        "expr": "default",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "p",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "you have #{friends} friends",
+                                    "line": 14,
+                                    "filename": "case.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": false,
+                              "line": 14,
+                              "filename": "case.tokens.json"
+                            }
+                          ],
+                          "line": 14,
+                          "filename": "case.tokens.json"
+                        },
+                        "debug": false,
+                        "line": 13,
+                        "filename": "case.tokens.json"
+                      }
+                    ],
+                    "line": 10,
+                    "filename": "case.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "case.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "case.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "case.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case.tokens.json
new file mode 100644
index 0000000..fdc7d4c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/case.tokens.json
@@ -0,0 +1,43 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"code","line":3,"val":"var friends = 1","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"case","line":4,"val":"friends"}
+{"type":"indent","line":5,"val":6}
+{"type":"when","line":5,"val":"0"}
+{"type":":","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"text","line":5,"val":"you have no friends"}
+{"type":"newline","line":6}
+{"type":"when","line":6,"val":"1"}
+{"type":":","line":6}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"text","line":6,"val":"you have a friend"}
+{"type":"newline","line":7}
+{"type":"default","line":7}
+{"type":":","line":7}
+{"type":"tag","line":7,"val":"p","selfClosing":false}
+{"type":"text","line":7,"val":"you have #{friends} friends"}
+{"type":"outdent","line":8}
+{"type":"code","line":8,"val":"var friends = 0","escape":false,"buffer":false}
+{"type":"newline","line":9}
+{"type":"case","line":9,"val":"friends"}
+{"type":"indent","line":10,"val":6}
+{"type":"when","line":10,"val":"0"}
+{"type":"newline","line":11}
+{"type":"when","line":11,"val":"1"}
+{"type":"indent","line":12,"val":8}
+{"type":"tag","line":12,"val":"p","selfClosing":false}
+{"type":"text","line":12,"val":"you have very few friends"}
+{"type":"outdent","line":13}
+{"type":"default","line":13}
+{"type":"indent","line":14,"val":8}
+{"type":"tag","line":14,"val":"p","selfClosing":false}
+{"type":"text","line":14,"val":"you have #{friends} friends"}
+{"type":"outdent","line":14}
+{"type":"outdent","line":14}
+{"type":"outdent","line":14}
+{"type":"outdent","line":14}
+{"type":"eos","line":14}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes-empty.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes-empty.expected.json
new file mode 100644
index 0000000..bb6487d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes-empty.expected.json
@@ -0,0 +1,67 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "''",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 1,
+      "filename": "classes-empty.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "null",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 2,
+      "filename": "classes-empty.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "undefined",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 3,
+      "filename": "classes-empty.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "classes-empty.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes-empty.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes-empty.tokens.json
new file mode 100644
index 0000000..da3dd3c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes-empty.tokens.json
@@ -0,0 +1,9 @@
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"class","val":"''","escaped":true}]}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"a","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"class","val":"null","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"class","val":"undefined","escaped":true}]}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes.expected.json
new file mode 100644
index 0000000..591f1cc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes.expected.json
@@ -0,0 +1,97 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "['foo', 'bar', 'baz']",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 1,
+      "filename": "classes.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'foo'",
+          "escaped": false
+        },
+        {
+          "name": "class",
+          "val": "'bar'",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'baz'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 5,
+      "filename": "classes.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'foo-bar_baz'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 9,
+      "filename": "classes.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "{foo: true, bar: false, baz: true}",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 11,
+      "filename": "classes.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "classes.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes.tokens.json
new file mode 100644
index 0000000..ac2f243
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/classes.tokens.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"class","val":"['foo', 'bar', 'baz']","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"a","selfClosing":false}
+{"type":"class","line":5,"val":"foo"}
+{"type":"attrs","line":5,"attrs":[{"name":"class","val":"'bar'","escaped":true}]}
+{"type":"class","line":5,"val":"baz"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"a","selfClosing":false}
+{"type":"class","line":9,"val":"foo-bar_baz"}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"a","selfClosing":false}
+{"type":"attrs","line":11,"attrs":[{"name":"class","val":"{foo: true, bar: false, baz: true}","escaped":true}]}
+{"type":"newline","line":12}
+{"type":"eos","line":12}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.conditionals.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.conditionals.expected.json
new file mode 100644
index 0000000..859d444
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.conditionals.expected.json
@@ -0,0 +1,503 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "if (true)",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 3,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 3,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "else",
+      "buffer": false,
+      "escape": false,
+      "line": 4,
+      "filename": "code.conditionals.tokens.json",
+      "debug": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 5,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 5,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 5,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "if (true) {",
+      "buffer": false,
+      "escape": false,
+      "line": 7,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 8,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 8,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "} else {",
+      "buffer": false,
+      "escape": false,
+      "line": 9,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 10,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 10,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 10,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "}",
+      "buffer": false,
+      "escape": false,
+      "line": 11,
+      "filename": "code.conditionals.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "if ( true)",
+      "line": 13,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 14,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 14,
+            "filename": "code.conditionals.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 15,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 15,
+            "filename": "code.conditionals.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "baz",
+                  "line": 16,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 16,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 14,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "else",
+      "line": 17,
+      "filename": "code.conditionals.tokens.json",
+      "debug": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 18,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 18,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 18,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "if (!( true))",
+      "line": 20,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 21,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 21,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 21,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "else",
+      "line": 22,
+      "filename": "code.conditionals.tokens.json",
+      "debug": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 23,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 23,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 23,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "if ( 'nested')",
+      "line": 25,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Code",
+            "val": "if ( 'works')",
+            "line": 26,
+            "filename": "code.conditionals.tokens.json",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "yay",
+                        "line": 27,
+                        "filename": "code.conditionals.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 27,
+                  "filename": "code.conditionals.tokens.json"
+                }
+              ],
+              "line": 27,
+              "filename": "code.conditionals.tokens.json"
+            }
+          }
+        ],
+        "line": 26,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Comment",
+      "val": " allow empty blocks",
+      "buffer": false,
+      "line": 29,
+      "filename": "code.conditionals.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "if ( false)",
+      "line": 30,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [],
+        "line": 30,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "else",
+      "line": 31,
+      "filename": "code.conditionals.tokens.json",
+      "debug": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'bar'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 32,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 32,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "if ( true)",
+      "line": 33,
+      "filename": "code.conditionals.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'bar'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 34,
+            "filename": "code.conditionals.tokens.json"
+          }
+        ],
+        "line": 34,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "else",
+      "line": 35,
+      "filename": "code.conditionals.tokens.json",
+      "debug": false,
+      "block": {
+        "type": "Block",
+        "nodes": [],
+        "line": 35,
+        "filename": "code.conditionals.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'bing'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 36,
+      "filename": "code.conditionals.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "code.conditionals.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.conditionals.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.conditionals.tokens.json
new file mode 100644
index 0000000..d3daa1c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.conditionals.tokens.json
@@ -0,0 +1,73 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"if (true)","escape":false,"buffer":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"text","line":3,"val":"foo"}
+{"type":"outdent","line":4}
+{"type":"code","line":4,"val":"else","escape":false,"buffer":false}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"text","line":5,"val":"bar"}
+{"type":"outdent","line":7}
+{"type":"code","line":7,"val":"if (true) {","escape":false,"buffer":false}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"foo"}
+{"type":"outdent","line":9}
+{"type":"code","line":9,"val":"} else {","escape":false,"buffer":false}
+{"type":"indent","line":10,"val":2}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"bar"}
+{"type":"outdent","line":11}
+{"type":"code","line":11,"val":"}","escape":false,"buffer":false}
+{"type":"newline","line":13}
+{"type":"code","line":13,"val":"if ( true)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":14,"val":2}
+{"type":"tag","line":14,"val":"p","selfClosing":false}
+{"type":"text","line":14,"val":"foo"}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"p","selfClosing":false}
+{"type":"text","line":15,"val":"bar"}
+{"type":"newline","line":16}
+{"type":"tag","line":16,"val":"p","selfClosing":false}
+{"type":"text","line":16,"val":"baz"}
+{"type":"outdent","line":17}
+{"type":"code","line":17,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"p","selfClosing":false}
+{"type":"text","line":18,"val":"bar"}
+{"type":"outdent","line":20}
+{"type":"code","line":20,"val":"if (!( true))","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":21,"val":2}
+{"type":"tag","line":21,"val":"p","selfClosing":false}
+{"type":"text","line":21,"val":"foo"}
+{"type":"outdent","line":22}
+{"type":"code","line":22,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":23,"val":2}
+{"type":"tag","line":23,"val":"p","selfClosing":false}
+{"type":"text","line":23,"val":"bar"}
+{"type":"outdent","line":25}
+{"type":"code","line":25,"val":"if ( 'nested')","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":26,"val":2}
+{"type":"code","line":26,"val":"if ( 'works')","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":27,"val":4}
+{"type":"tag","line":27,"val":"p","selfClosing":false}
+{"type":"text","line":27,"val":"yay"}
+{"type":"outdent","line":29}
+{"type":"outdent","line":29}
+{"type":"comment","line":29,"val":" allow empty blocks","buffer":false}
+{"type":"newline","line":30}
+{"type":"code","line":30,"val":"if ( false)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"newline","line":31}
+{"type":"code","line":31,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":32,"val":2}
+{"type":"class","line":32,"val":"bar"}
+{"type":"outdent","line":33}
+{"type":"code","line":33,"val":"if ( true)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":34,"val":2}
+{"type":"class","line":34,"val":"bar"}
+{"type":"outdent","line":35}
+{"type":"code","line":35,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"newline","line":36}
+{"type":"class","line":36,"val":"bing"}
+{"type":"eos","line":36}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.escape.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.escape.expected.json
new file mode 100644
index 0000000..10cbf69
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.escape.expected.json
@@ -0,0 +1,51 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "code.escape.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "'<script>'",
+        "buffer": true,
+        "escape": true,
+        "line": 1,
+        "filename": "code.escape.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "code.escape.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "'<script>'",
+        "buffer": true,
+        "escape": false,
+        "line": 2,
+        "filename": "code.escape.tokens.json"
+      }
+    }
+  ],
+  "line": 0,
+  "filename": "code.escape.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.escape.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.escape.tokens.json
new file mode 100644
index 0000000..f6a4ecf
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.escape.tokens.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"code","line":1,"val":"'<script>'","escape":true,"buffer":true}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"code","line":2,"val":"'<script>'","escape":false,"buffer":true}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.expected.json
new file mode 100644
index 0000000..f1e9b1a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.expected.json
@@ -0,0 +1,217 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "code.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "null",
+        "buffer": true,
+        "escape": true,
+        "line": 1,
+        "filename": "code.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "code.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "undefined",
+        "buffer": true,
+        "escape": true,
+        "line": 2,
+        "filename": "code.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "code.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "''",
+        "buffer": true,
+        "escape": true,
+        "line": 3,
+        "filename": "code.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "code.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "0",
+        "buffer": true,
+        "escape": true,
+        "line": 4,
+        "filename": "code.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "code.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "false",
+        "buffer": true,
+        "escape": true,
+        "line": 5,
+        "filename": "code.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "null",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "code.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "undefined",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 7,
+      "filename": "code.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "''",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 8,
+      "filename": "code.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "0",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 9,
+      "filename": "code.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "false",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 10,
+      "filename": "code.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "code.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.iteration.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.iteration.expected.json
new file mode 100644
index 0000000..b4d3987
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.iteration.expected.json
@@ -0,0 +1,394 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var items = [1,2,3]",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Code",
+            "val": "items.forEach(function(item){",
+            "buffer": false,
+            "escape": false,
+            "line": 5,
+            "filename": "code.iteration.tokens.json",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "code.iteration.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "item",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 6,
+                    "filename": "code.iteration.tokens.json"
+                  }
+                }
+              ],
+              "line": 6,
+              "filename": "code.iteration.tokens.json"
+            }
+          },
+          {
+            "type": "Code",
+            "val": "})",
+            "buffer": false,
+            "escape": false,
+            "line": 7,
+            "filename": "code.iteration.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var items = [1,2,3]",
+      "buffer": false,
+      "escape": false,
+      "line": 9,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "items",
+            "val": "item",
+            "key": "i",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "class",
+                      "val": "'item-' + (i) + ''",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 13,
+                  "filename": "code.iteration.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "item",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 13,
+                    "filename": "code.iteration.tokens.json"
+                  }
+                }
+              ],
+              "line": 13,
+              "filename": "code.iteration.tokens.json"
+            },
+            "line": 12,
+            "filename": "code.iteration.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 11,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "items",
+            "val": "item",
+            "key": "i",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 17,
+                  "filename": "code.iteration.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "item",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 17,
+                    "filename": "code.iteration.tokens.json"
+                  }
+                }
+              ],
+              "line": 17,
+              "filename": "code.iteration.tokens.json"
+            },
+            "line": 16,
+            "filename": "code.iteration.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 15,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "items",
+            "val": "$item",
+            "key": "$index",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 21,
+                  "filename": "code.iteration.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "$item",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 21,
+                    "filename": "code.iteration.tokens.json"
+                  }
+                }
+              ],
+              "line": 21,
+              "filename": "code.iteration.tokens.json"
+            },
+            "line": 20,
+            "filename": "code.iteration.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 19,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var nums = [1, 2, 3]",
+      "buffer": false,
+      "escape": false,
+      "line": 23,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var letters = ['a', 'b', 'c']",
+      "buffer": false,
+      "escape": false,
+      "line": 24,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "letters",
+            "val": "l",
+            "key": "$index",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Each",
+                  "obj": "nums",
+                  "val": "n",
+                  "key": "$index",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "li",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "#{n}: #{l}",
+                              "line": 29,
+                              "filename": "code.iteration.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 29,
+                        "filename": "code.iteration.tokens.json"
+                      }
+                    ],
+                    "line": 29,
+                    "filename": "code.iteration.tokens.json"
+                  },
+                  "line": 28,
+                  "filename": "code.iteration.tokens.json"
+                }
+              ],
+              "line": 28,
+              "filename": "code.iteration.tokens.json"
+            },
+            "line": 27,
+            "filename": "code.iteration.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 26,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var count = 1",
+      "buffer": false,
+      "escape": false,
+      "line": 31,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var counter = function() { return [count++, count++, count++] }",
+      "buffer": false,
+      "escape": false,
+      "line": 32,
+      "filename": "code.iteration.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "counter()",
+            "val": "n",
+            "key": "$index",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "#{n}",
+                        "line": 35,
+                        "filename": "code.iteration.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 35,
+                  "filename": "code.iteration.tokens.json"
+                }
+              ],
+              "line": 35,
+              "filename": "code.iteration.tokens.json"
+            },
+            "line": 34,
+            "filename": "code.iteration.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 33,
+      "filename": "code.iteration.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "code.iteration.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.iteration.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.iteration.tokens.json
new file mode 100644
index 0000000..a12c2a4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.iteration.tokens.json
@@ -0,0 +1,67 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var items = [1,2,3]","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"ul","selfClosing":false}
+{"type":"indent","line":5,"val":2}
+{"type":"code","line":5,"val":"items.forEach(function(item){","escape":false,"buffer":false}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"code","line":6,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"code","line":7,"val":"})","escape":false,"buffer":false}
+{"type":"outdent","line":9}
+{"type":"code","line":9,"val":"var items = [1,2,3]","escape":false,"buffer":false}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"ul","selfClosing":false}
+{"type":"indent","line":12,"val":2}
+{"type":"each","line":12,"val":"item","key":"i","code":"items"}
+{"type":"indent","line":13,"val":4}
+{"type":"tag","line":13,"val":"li","selfClosing":false}
+{"type":"attrs","line":13,"attrs":[{"name":"class","val":"'item-' + (i) + ''","escaped":true}]}
+{"type":"code","line":13,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"tag","line":15,"val":"ul","selfClosing":false}
+{"type":"indent","line":16,"val":2}
+{"type":"each","line":16,"val":"item","key":"i","code":"items"}
+{"type":"indent","line":17,"val":4}
+{"type":"tag","line":17,"val":"li","selfClosing":false}
+{"type":"code","line":17,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":19}
+{"type":"outdent","line":19}
+{"type":"tag","line":19,"val":"ul","selfClosing":false}
+{"type":"indent","line":20,"val":2}
+{"type":"each","line":20,"val":"$item","key":"$index","code":"items"}
+{"type":"indent","line":21,"val":4}
+{"type":"tag","line":21,"val":"li","selfClosing":false}
+{"type":"code","line":21,"val":"$item","escape":true,"buffer":true}
+{"type":"outdent","line":23}
+{"type":"outdent","line":23}
+{"type":"code","line":23,"val":"var nums = [1, 2, 3]","escape":false,"buffer":false}
+{"type":"newline","line":24}
+{"type":"code","line":24,"val":"var letters = ['a', 'b', 'c']","escape":false,"buffer":false}
+{"type":"newline","line":26}
+{"type":"tag","line":26,"val":"ul","selfClosing":false}
+{"type":"indent","line":27,"val":2}
+{"type":"each","line":27,"val":"l","key":"$index","code":"letters"}
+{"type":"indent","line":28,"val":4}
+{"type":"each","line":28,"val":"n","key":"$index","code":"nums"}
+{"type":"indent","line":29,"val":6}
+{"type":"tag","line":29,"val":"li","selfClosing":false}
+{"type":"text","line":29,"val":"#{n}: #{l}"}
+{"type":"outdent","line":31}
+{"type":"outdent","line":31}
+{"type":"outdent","line":31}
+{"type":"code","line":31,"val":"var count = 1","escape":false,"buffer":false}
+{"type":"newline","line":32}
+{"type":"code","line":32,"val":"var counter = function() { return [count++, count++, count++] }","escape":false,"buffer":false}
+{"type":"newline","line":33}
+{"type":"tag","line":33,"val":"ul","selfClosing":false}
+{"type":"indent","line":34,"val":2}
+{"type":"each","line":34,"val":"n","key":"$index","code":"counter()"}
+{"type":"indent","line":35,"val":4}
+{"type":"tag","line":35,"val":"li","selfClosing":false}
+{"type":"text","line":35,"val":"#{n}"}
+{"type":"outdent","line":35}
+{"type":"outdent","line":35}
+{"type":"eos","line":35}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.tokens.json
new file mode 100644
index 0000000..eb6265a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/code.tokens.json
@@ -0,0 +1,30 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"code","line":1,"val":"null","escape":true,"buffer":true}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"code","line":2,"val":"undefined","escape":true,"buffer":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"code","line":3,"val":"''","escape":true,"buffer":true}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"code","line":4,"val":"0","escape":true,"buffer":true}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"code","line":5,"val":"false","escape":true,"buffer":true}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"foo","val":"null","escaped":true}]}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"p","selfClosing":false}
+{"type":"attrs","line":7,"attrs":[{"name":"foo","val":"undefined","escaped":true}]}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"foo","val":"''","escaped":true}]}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"foo","val":"0","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"attrs","line":10,"attrs":[{"name":"foo","val":"false","escaped":true}]}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.expected.json
new file mode 100644
index 0000000..7fdcc61
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.expected.json
@@ -0,0 +1,300 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Comment",
+      "val": " foo",
+      "buffer": true,
+      "line": 2,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Comment",
+            "val": " bar",
+            "buffer": true,
+            "line": 4,
+            "filename": "comments.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "one",
+                  "line": 5,
+                  "filename": "comments.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 5,
+            "filename": "comments.tokens.json"
+          },
+          {
+            "type": "Comment",
+            "val": " baz",
+            "buffer": true,
+            "line": 6,
+            "filename": "comments.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "two",
+                  "line": 7,
+                  "filename": "comments.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 7,
+            "filename": "comments.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "BlockComment",
+      "val": "",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "ul",
+            "line": 10
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 11
+          },
+          {
+            "type": "Text",
+            "val": "  li foo",
+            "line": 11
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 12
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 12
+          }
+        ]
+      },
+      "buffer": true,
+      "line": 9,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "BlockComment",
+      "val": " block",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "// inline follow",
+            "line": 14
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 15
+          },
+          {
+            "type": "Text",
+            "val": "li three",
+            "line": 15
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 16
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 16
+          }
+        ]
+      },
+      "buffer": true,
+      "line": 13,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "BlockComment",
+      "val": " block",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "// inline followed by tags",
+            "line": 18
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 19
+          },
+          {
+            "type": "Text",
+            "val": "ul",
+            "line": 19
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 20
+          },
+          {
+            "type": "Text",
+            "val": "  li four",
+            "line": 20
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 21
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 21
+          }
+        ]
+      },
+      "buffer": true,
+      "line": 17,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "BlockComment",
+      "val": "if IE lt 9",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "// inline",
+            "line": 23
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 24
+          },
+          {
+            "type": "Text",
+            "val": "script(src='/lame.js')",
+            "line": 24
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 25
+          },
+          {
+            "type": "Text",
+            "val": "// end-inline",
+            "line": 25
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 26
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 26
+          }
+        ]
+      },
+      "buffer": true,
+      "line": 22,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "five",
+            "line": 27,
+            "filename": "comments.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 27,
+      "filename": "comments.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "// not a comment",
+            "line": 29,
+            "filename": "comments.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'foo'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 29,
+      "filename": "comments.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "comments.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.source.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.source.expected.json
new file mode 100644
index 0000000..ab41255
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.source.expected.json
@@ -0,0 +1,84 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "BlockComment",
+      "val": "",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "s/s.",
+            "line": 2
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 3
+          }
+        ]
+      },
+      "buffer": false,
+      "line": 1,
+      "filename": "comments.source.tokens.json"
+    },
+    {
+      "type": "Comment",
+      "val": " test/cases/comments.source.jade",
+      "buffer": false,
+      "line": 4,
+      "filename": "comments.source.tokens.json"
+    },
+    {
+      "type": "Text",
+      "val": "",
+      "line": 5,
+      "filename": "comments.source.tokens.json"
+    },
+    {
+      "type": "BlockComment",
+      "val": "",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "test/cases/comments.source.jade",
+            "line": 7
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 8
+          },
+          {
+            "type": "Text",
+            "val": "when",
+            "line": 8
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 9
+          },
+          {
+            "type": "Text",
+            "val": "()",
+            "line": 9
+          }
+        ]
+      },
+      "buffer": false,
+      "line": 6,
+      "filename": "comments.source.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "comments.source.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.source.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.source.tokens.json
new file mode 100644
index 0000000..73610d1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.source.tokens.json
@@ -0,0 +1,19 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"s/s."}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":""}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"comment","line":4,"val":" test/cases/comments.source.jade","buffer":false}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"test/cases/comments.source.jade"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"when"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"()"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.tokens.json
new file mode 100644
index 0000000..ec2a443
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/comments.tokens.json
@@ -0,0 +1,61 @@
+{"type":"newline","line":2}
+{"type":"comment","line":2,"val":" foo","buffer":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"comment","line":4,"val":" bar","buffer":true}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"text","line":5,"val":"one"}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":" baz","buffer":true}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"two"}
+{"type":"outdent","line":9}
+{"type":"comment","line":9,"val":"","buffer":true}
+{"type":"start-pipeless-text","line":9}
+{"type":"text","line":10,"val":"ul"}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"  li foo"}
+{"type":"newline","line":12}
+{"type":"text","line":12,"val":""}
+{"type":"end-pipeless-text","line":12}
+{"type":"newline","line":13}
+{"type":"comment","line":13,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":13}
+{"type":"text","line":14,"val":"// inline follow"}
+{"type":"newline","line":15}
+{"type":"text","line":15,"val":"li three"}
+{"type":"newline","line":16}
+{"type":"text","line":16,"val":""}
+{"type":"end-pipeless-text","line":16}
+{"type":"newline","line":17}
+{"type":"comment","line":17,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":17}
+{"type":"text","line":18,"val":"// inline followed by tags"}
+{"type":"newline","line":19}
+{"type":"text","line":19,"val":"ul"}
+{"type":"newline","line":20}
+{"type":"text","line":20,"val":"  li four"}
+{"type":"newline","line":21}
+{"type":"text","line":21,"val":""}
+{"type":"end-pipeless-text","line":21}
+{"type":"newline","line":22}
+{"type":"comment","line":22,"val":"if IE lt 9","buffer":true}
+{"type":"start-pipeless-text","line":22}
+{"type":"text","line":23,"val":"// inline"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"script(src='/lame.js')"}
+{"type":"newline","line":25}
+{"type":"text","line":25,"val":"// end-inline"}
+{"type":"newline","line":26}
+{"type":"text","line":26,"val":""}
+{"type":"end-pipeless-text","line":26}
+{"type":"newline","line":27}
+{"type":"tag","line":27,"val":"p","selfClosing":false}
+{"type":"text","line":27,"val":"five"}
+{"type":"newline","line":29}
+{"type":"class","line":29,"val":"foo"}
+{"type":"text","line":29,"val":"// not a comment"}
+{"type":"eos","line":29}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/custom-filter.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/custom-filter.expected.json
new file mode 100644
index 0000000..c6fb362
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/custom-filter.expected.json
@@ -0,0 +1,30 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Filter",
+      "name": "custom-filter",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "foo bar",
+            "line": 2
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "'bar'",
+          "escaped": true
+        }
+      ],
+      "line": 1,
+      "filename": "custom-filter.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "custom-filter.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/custom-filter.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/custom-filter.tokens.json
new file mode 100644
index 0000000..667d80a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/custom-filter.tokens.json
@@ -0,0 +1,6 @@
+{"type":"filter","line":1,"val":"custom-filter"}
+{"type":"attrs","line":1,"attrs":[{"name":"foo","val":"'bar'","escaped":true}]}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"foo bar"}
+{"type":"end-pipeless-text","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.custom.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.custom.expected.json
new file mode 100644
index 0000000..0173e53
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.custom.expected.json
@@ -0,0 +1,13 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Doctype",
+      "val": "custom stuff",
+      "line": 1,
+      "filename": "doctype.custom.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "doctype.custom.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.custom.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.custom.tokens.json
new file mode 100644
index 0000000..aacb862
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.custom.tokens.json
@@ -0,0 +1,2 @@
+{"type":"doctype","line":1,"val":"custom stuff"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.default.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.default.expected.json
new file mode 100644
index 0000000..e0e3028
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.default.expected.json
@@ -0,0 +1,63 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Doctype",
+      "line": 1,
+      "filename": "doctype.default.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "Title",
+                        "line": 4,
+                        "filename": "doctype.default.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "doctype.default.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "doctype.default.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "doctype.default.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "doctype.default.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.default.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.default.tokens.json
new file mode 100644
index 0000000..c46ad8a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.default.tokens.json
@@ -0,0 +1,11 @@
+{"type":"doctype","line":1}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"indent","line":4,"val":4}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"text","line":4,"val":"Title"}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.keyword.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.keyword.expected.json
new file mode 100644
index 0000000..9436bef
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.keyword.expected.json
@@ -0,0 +1,13 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Doctype",
+      "val": "html",
+      "line": 1,
+      "filename": "doctype.keyword.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "doctype.keyword.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.keyword.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.keyword.tokens.json
new file mode 100644
index 0000000..3c5ac9d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/doctype.keyword.tokens.json
@@ -0,0 +1,2 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/each.else.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/each.else.expected.json
new file mode 100644
index 0000000..491e22b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/each.else.expected.json
@@ -0,0 +1,544 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var users = []",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "users",
+            "val": "user",
+            "key": "$index",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "each.else.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "user.name",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 6,
+                    "filename": "each.else.tokens.json"
+                  }
+                }
+              ],
+              "line": 6,
+              "filename": "each.else.tokens.json"
+            },
+            "line": 5,
+            "filename": "each.else.tokens.json",
+            "alternative": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "no users!",
+                        "line": 8,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 8,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 8,
+              "filename": "each.else.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var users = [{ name: 'tobi', friends: ['loki'] }, { name: 'loki' }]",
+      "buffer": false,
+      "escape": false,
+      "line": 11,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "if ( users)",
+      "line": 13,
+      "filename": "each.else.tokens.json",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "ul",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Each",
+                  "obj": "users",
+                  "val": "user",
+                  "key": "$index",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "li",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 16,
+                        "filename": "each.else.tokens.json",
+                        "code": {
+                          "type": "Code",
+                          "val": "user.name",
+                          "buffer": true,
+                          "escape": true,
+                          "line": 16,
+                          "filename": "each.else.tokens.json"
+                        }
+                      }
+                    ],
+                    "line": 16,
+                    "filename": "each.else.tokens.json"
+                  },
+                  "line": 15,
+                  "filename": "each.else.tokens.json",
+                  "alternative": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "li",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "no users!",
+                              "line": 18,
+                              "filename": "each.else.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 18,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ],
+                    "line": 18,
+                    "filename": "each.else.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 14,
+            "filename": "each.else.tokens.json"
+          }
+        ],
+        "line": 14,
+        "filename": "each.else.tokens.json"
+      }
+    },
+    {
+      "type": "Code",
+      "val": "var user = { name: 'tobi', age: 10 }",
+      "buffer": false,
+      "escape": false,
+      "line": 20,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "user",
+            "val": "val",
+            "key": "key",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "#{key}: #{val}",
+                        "line": 24,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 24,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 24,
+              "filename": "each.else.tokens.json"
+            },
+            "line": 23,
+            "filename": "each.else.tokens.json",
+            "alternative": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "user has no details!",
+                        "line": 26,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 26,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 26,
+              "filename": "each.else.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 22,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var user = {}",
+      "buffer": false,
+      "escape": false,
+      "line": 28,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "user",
+            "val": "prop",
+            "key": "key",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "#{key}: #{val}",
+                        "line": 32,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 32,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 32,
+              "filename": "each.else.tokens.json"
+            },
+            "line": 31,
+            "filename": "each.else.tokens.json",
+            "alternative": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "user has no details!",
+                        "line": 34,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 34,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 34,
+              "filename": "each.else.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 30,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "user",
+            "val": "prop",
+            "key": "key",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "#{key}: #{val}",
+                        "line": 38,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 38,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 38,
+              "filename": "each.else.tokens.json"
+            },
+            "line": 37,
+            "filename": "each.else.tokens.json",
+            "alternative": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "user has no details!",
+                        "line": 40,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 40,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 40,
+              "filename": "each.else.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 36,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var user = Object.create(null)",
+      "buffer": false,
+      "escape": false,
+      "line": 42,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "user.name = 'tobi'",
+      "buffer": false,
+      "escape": false,
+      "line": 43,
+      "filename": "each.else.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "user",
+            "val": "val",
+            "key": "key",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "#{key}: #{val}",
+                        "line": 47,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 47,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 47,
+              "filename": "each.else.tokens.json"
+            },
+            "line": 46,
+            "filename": "each.else.tokens.json",
+            "alternative": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "user has no details!",
+                        "line": 49,
+                        "filename": "each.else.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 49,
+                  "filename": "each.else.tokens.json"
+                }
+              ],
+              "line": 49,
+              "filename": "each.else.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 45,
+      "filename": "each.else.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "each.else.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/each.else.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/each.else.tokens.json
new file mode 100644
index 0000000..c23ed1c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/each.else.tokens.json
@@ -0,0 +1,95 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var users = []","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"ul","selfClosing":false}
+{"type":"indent","line":5,"val":2}
+{"type":"each","line":5,"val":"user","key":"$index","code":"users"}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"code","line":6,"val":"user.name","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"code","line":7,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":8,"val":4}
+{"type":"tag","line":8,"val":"li","selfClosing":false}
+{"type":"text","line":8,"val":"no users!"}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"code","line":11,"val":"var users = [{ name: 'tobi', friends: ['loki'] }, { name: 'loki' }]","escape":false,"buffer":false}
+{"type":"newline","line":13}
+{"type":"code","line":13,"val":"if ( users)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":14,"val":2}
+{"type":"tag","line":14,"val":"ul","selfClosing":false}
+{"type":"indent","line":15,"val":4}
+{"type":"each","line":15,"val":"user","key":"$index","code":"users"}
+{"type":"indent","line":16,"val":6}
+{"type":"tag","line":16,"val":"li","selfClosing":false}
+{"type":"code","line":16,"val":"user.name","escape":true,"buffer":true}
+{"type":"outdent","line":17}
+{"type":"code","line":17,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":18,"val":6}
+{"type":"tag","line":18,"val":"li","selfClosing":false}
+{"type":"text","line":18,"val":"no users!"}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"code","line":20,"val":"var user = { name: 'tobi', age: 10 }","escape":false,"buffer":false}
+{"type":"newline","line":22}
+{"type":"tag","line":22,"val":"ul","selfClosing":false}
+{"type":"indent","line":23,"val":2}
+{"type":"each","line":23,"val":"val","key":"key","code":"user"}
+{"type":"indent","line":24,"val":4}
+{"type":"tag","line":24,"val":"li","selfClosing":false}
+{"type":"text","line":24,"val":"#{key}: #{val}"}
+{"type":"outdent","line":25}
+{"type":"code","line":25,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":26,"val":4}
+{"type":"tag","line":26,"val":"li","selfClosing":false}
+{"type":"text","line":26,"val":"user has no details!"}
+{"type":"outdent","line":28}
+{"type":"outdent","line":28}
+{"type":"code","line":28,"val":"var user = {}","escape":false,"buffer":false}
+{"type":"newline","line":30}
+{"type":"tag","line":30,"val":"ul","selfClosing":false}
+{"type":"indent","line":31,"val":2}
+{"type":"each","line":31,"val":"prop","key":"key","code":"user"}
+{"type":"indent","line":32,"val":4}
+{"type":"tag","line":32,"val":"li","selfClosing":false}
+{"type":"text","line":32,"val":"#{key}: #{val}"}
+{"type":"outdent","line":33}
+{"type":"code","line":33,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":34,"val":4}
+{"type":"tag","line":34,"val":"li","selfClosing":false}
+{"type":"text","line":34,"val":"user has no details!"}
+{"type":"outdent","line":36}
+{"type":"outdent","line":36}
+{"type":"tag","line":36,"val":"ul","selfClosing":false}
+{"type":"indent","line":37,"val":2}
+{"type":"each","line":37,"val":"prop","key":"key","code":"user"}
+{"type":"indent","line":38,"val":4}
+{"type":"tag","line":38,"val":"li","selfClosing":false}
+{"type":"text","line":38,"val":"#{key}: #{val}"}
+{"type":"outdent","line":39}
+{"type":"code","line":39,"val":"else","escape":false,"buffer":false}
+{"type":"indent","line":40,"val":4}
+{"type":"tag","line":40,"val":"li","selfClosing":false}
+{"type":"text","line":40,"val":"user has no details!"}
+{"type":"outdent","line":42}
+{"type":"outdent","line":42}
+{"type":"code","line":42,"val":"var user = Object.create(null)","escape":false,"buffer":false}
+{"type":"newline","line":43}
+{"type":"code","line":43,"val":"user.name = 'tobi'","escape":false,"buffer":false}
+{"type":"newline","line":45}
+{"type":"tag","line":45,"val":"ul","selfClosing":false}
+{"type":"indent","line":46,"val":2}
+{"type":"each","line":46,"val":"val","key":"key","code":"user"}
+{"type":"indent","line":47,"val":4}
+{"type":"tag","line":47,"val":"li","selfClosing":false}
+{"type":"text","line":47,"val":"#{key}: #{val}"}
+{"type":"outdent","line":48}
+{"type":"code","line":48,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":49,"val":4}
+{"type":"tag","line":49,"val":"li","selfClosing":false}
+{"type":"text","line":49,"val":"user has no details!"}
+{"type":"outdent","line":50}
+{"type":"outdent","line":50}
+{"type":"eos","line":50}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-chars.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-chars.expected.json
new file mode 100644
index 0000000..30b8fd8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-chars.expected.json
@@ -0,0 +1,28 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "var re = /\\d+/;",
+            "line": 2
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "escape-chars.tokens.json",
+      "textOnly": true
+    }
+  ],
+  "line": 0,
+  "filename": "escape-chars.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-chars.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-chars.tokens.json
new file mode 100644
index 0000000..a97de6c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-chars.tokens.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"var re = /\\d+/;"}
+{"type":"end-pipeless-text","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-test.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-test.expected.json
new file mode 100644
index 0000000..d8d84bd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-test.expected.json
@@ -0,0 +1,108 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Doctype",
+      "val": "html",
+      "line": 1,
+      "filename": "escape-test.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "head",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "title",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "escape-test",
+                        "line": 4,
+                        "filename": "escape-test.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "escape-test.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "escape-test.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "textarea",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Code",
+                        "val": "var txt = '<param name=\"flashvars\" value=\"a=&quot;value_a&quot;&b=&quot;value_b&quot;&c=3\"/>'",
+                        "buffer": false,
+                        "escape": false,
+                        "line": 7,
+                        "filename": "escape-test.tokens.json"
+                      },
+                      {
+                        "type": "Text",
+                        "val": "#{txt}",
+                        "line": 8,
+                        "filename": "escape-test.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "escape-test.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 5,
+            "filename": "escape-test.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "escape-test.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "escape-test.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-test.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-test.tokens.json
new file mode 100644
index 0000000..9efc842
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escape-test.tokens.json
@@ -0,0 +1,20 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"head","selfClosing":false}
+{"type":"indent","line":4,"val":8}
+{"type":"tag","line":4,"val":"title","selfClosing":false}
+{"type":"text","line":4,"val":"escape-test"}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"body","selfClosing":false}
+{"type":"indent","line":6,"val":8}
+{"type":"tag","line":6,"val":"textarea","selfClosing":false}
+{"type":"indent","line":7,"val":12}
+{"type":"code","line":7,"val":"var txt = '<param name=\"flashvars\" value=\"a=&quot;value_a&quot;&b=&quot;value_b&quot;&c=3\"/>'","escape":false,"buffer":false}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"#{txt}"}
+{"type":"outdent","line":9}
+{"type":"outdent","line":9}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escaping-class-attribute.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escaping-class-attribute.expected.json
new file mode 100644
index 0000000..a73e93e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escaping-class-attribute.expected.json
@@ -0,0 +1,127 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "attr",
+          "val": "\"<%= bar %>\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "escaping-class-attribute.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "\"<%= bar %>\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "escaping-class-attribute.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "attr",
+          "val": "\"<%= bar %>\"",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "escaping-class-attribute.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "\"<%= bar %>\"",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "escaping-class-attribute.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "\"<%= bar %> lol rofl\"",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "escaping-class-attribute.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "\"<%= bar %> lol rofl <%= lmao %>\"",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "escaping-class-attribute.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "escaping-class-attribute.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escaping-class-attribute.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escaping-class-attribute.tokens.json
new file mode 100644
index 0000000..c76e658
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/escaping-class-attribute.tokens.json
@@ -0,0 +1,19 @@
+{"type":"tag","line":1,"val":"foo","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"attr","val":"\"<%= bar %>\"","escaped":true}]}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"foo","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"class","val":"\"<%= bar %>\"","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"attr","val":"\"<%= bar %>\"","escaped":false}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"foo","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"class","val":"\"<%= bar %>\"","escaped":false}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"foo","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"class","val":"\"<%= bar %> lol rofl\"","escaped":false}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"foo","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"class","val":"\"<%= bar %> lol rofl <%= lmao %>\"","escaped":false}]}
+{"type":"newline","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters-empty.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters-empty.expected.json
new file mode 100644
index 0000000..8e68a24
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters-empty.expected.json
@@ -0,0 +1,79 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var users = [{ name: 'tobi', age: 2 }]",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "filters-empty.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "fb:users",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "users",
+            "val": "user",
+            "key": "$index",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "fb:user",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Filter",
+                        "name": "cdata",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [],
+                          "line": 6,
+                          "filename": "filters-empty.tokens.json"
+                        },
+                        "attrs": [],
+                        "line": 6,
+                        "filename": "filters-empty.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "age",
+                      "val": "user.age",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 5,
+                  "filename": "filters-empty.tokens.json"
+                }
+              ],
+              "line": 5,
+              "filename": "filters-empty.tokens.json"
+            },
+            "line": 4,
+            "filename": "filters-empty.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "filters-empty.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "filters-empty.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters-empty.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters-empty.tokens.json
new file mode 100644
index 0000000..6a8b691
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters-empty.tokens.json
@@ -0,0 +1,14 @@
+{"type":"code","line":1,"val":"var users = [{ name: 'tobi', age: 2 }]","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"fb:users","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"each","line":4,"val":"user","key":"$index","code":"users"}
+{"type":"indent","line":5,"val":4}
+{"type":"tag","line":5,"val":"fb:user","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"age","val":"user.age","escaped":true}]}
+{"type":"indent","line":6,"val":6}
+{"type":"filter","line":6,"val":"cdata"}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.cdata.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.cdata.expected.json
new file mode 100644
index 0000000..f4cb28d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.cdata.expected.json
@@ -0,0 +1,83 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "users = [{ name: 'tobi', age: 2 }]",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "filters.cdata.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "fb:users",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Each",
+            "obj": "users",
+            "val": "user",
+            "key": "$index",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "fb:user",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Filter",
+                        "name": "cdata",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "#{user.name}",
+                              "line": 8
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "line": 7,
+                        "filename": "filters.cdata.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "age",
+                      "val": "user.age",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "filters.cdata.tokens.json"
+                }
+              ],
+              "line": 6,
+              "filename": "filters.cdata.tokens.json"
+            },
+            "line": 5,
+            "filename": "filters.cdata.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "filters.cdata.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "filters.cdata.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.cdata.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.cdata.tokens.json
new file mode 100644
index 0000000..3320ae8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.cdata.tokens.json
@@ -0,0 +1,18 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"users = [{ name: 'tobi', age: 2 }]","escape":false,"buffer":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"fb:users","selfClosing":false}
+{"type":"indent","line":5,"val":2}
+{"type":"each","line":5,"val":"user","key":"$index","code":"users"}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"fb:user","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"age","val":"user.age","escaped":true}]}
+{"type":"indent","line":7,"val":6}
+{"type":"filter","line":7,"val":"cdata"}
+{"type":"start-pipeless-text","line":7}
+{"type":"text","line":8,"val":"#{user.name}"}
+{"type":"end-pipeless-text","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.coffeescript.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.coffeescript.expected.json
new file mode 100644
index 0000000..b9772a6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.coffeescript.expected.json
@@ -0,0 +1,78 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Filter",
+            "name": "coffeescript",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "regexp = /\\n/",
+                  "line": 3
+                }
+              ]
+            },
+            "attrs": [],
+            "line": 2,
+            "filename": "filters.coffeescript.tokens.json"
+          },
+          {
+            "type": "Filter",
+            "name": "coffeescript",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "math =",
+                  "line": 5
+                },
+                {
+                  "type": "Text",
+                  "val": "\n",
+                  "line": 6
+                },
+                {
+                  "type": "Text",
+                  "val": "  square: (value) -> value * value",
+                  "line": 6
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "minify",
+                "val": "true",
+                "escaped": true
+              }
+            ],
+            "line": 4,
+            "filename": "filters.coffeescript.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'text/javascript'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "filters.coffeescript.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "filters.coffeescript.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.coffeescript.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.coffeescript.tokens.json
new file mode 100644
index 0000000..af13e96
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.coffeescript.tokens.json
@@ -0,0 +1,17 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/javascript'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"filter","line":2,"val":"coffeescript"}
+{"type":"start-pipeless-text","line":2}
+{"type":"text","line":3,"val":"regexp = /\\n/"}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"filter","line":4,"val":"coffeescript"}
+{"type":"attrs","line":4,"attrs":[{"name":"minify","val":"true","escaped":true}]}
+{"type":"start-pipeless-text","line":4}
+{"type":"text","line":5,"val":"math ="}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"  square: (value) -> value * value"}
+{"type":"end-pipeless-text","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.less.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.less.expected.json
new file mode 100644
index 0000000..81815c0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.less.expected.json
@@ -0,0 +1,105 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "head",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "style",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Filter",
+                        "name": "less",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "@pad: 15px;",
+                              "line": 5
+                            },
+                            {
+                              "type": "Text",
+                              "val": "\n",
+                              "line": 6
+                            },
+                            {
+                              "type": "Text",
+                              "val": "body {",
+                              "line": 6
+                            },
+                            {
+                              "type": "Text",
+                              "val": "\n",
+                              "line": 7
+                            },
+                            {
+                              "type": "Text",
+                              "val": "  padding: @pad;",
+                              "line": 7
+                            },
+                            {
+                              "type": "Text",
+                              "val": "\n",
+                              "line": 8
+                            },
+                            {
+                              "type": "Text",
+                              "val": "}",
+                              "line": 8
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "line": 4,
+                        "filename": "filters.less.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "type",
+                      "val": "\"text/css\"",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "filters.less.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "filters.less.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "filters.less.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "filters.less.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.less.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.less.tokens.json
new file mode 100644
index 0000000..7301cd1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.less.tokens.json
@@ -0,0 +1,21 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"style","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"\"text/css\"","escaped":true}]}
+{"type":"indent","line":4,"val":6}
+{"type":"filter","line":4,"val":"less"}
+{"type":"start-pipeless-text","line":4}
+{"type":"text","line":5,"val":"@pad: 15px;"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"body {"}
+{"type":"newline","line":7}
+{"type":"text","line":7,"val":"  padding: @pad;"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"}"}
+{"type":"end-pipeless-text","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.markdown.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.markdown.expected.json
new file mode 100644
index 0000000..5a27bce
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.markdown.expected.json
@@ -0,0 +1,64 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Filter",
+                  "name": "markdown",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "This is _some_ awesome **markdown**",
+                        "line": 4
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 5
+                      },
+                      {
+                        "type": "Text",
+                        "val": "whoop.",
+                        "line": 5
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "line": 3,
+                  "filename": "filters.markdown.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "filters.markdown.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "filters.markdown.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "filters.markdown.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.markdown.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.markdown.tokens.json
new file mode 100644
index 0000000..0dc68f7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.markdown.tokens.json
@@ -0,0 +1,13 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"filter","line":3,"val":"markdown"}
+{"type":"start-pipeless-text","line":3}
+{"type":"text","line":4,"val":"This is _some_ awesome **markdown**"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"whoop."}
+{"type":"end-pipeless-text","line":5}
+{"type":"outdent","line":5}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.stylus.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.stylus.expected.json
new file mode 100644
index 0000000..7378d6f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.stylus.expected.json
@@ -0,0 +1,99 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "head",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "style",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Filter",
+                        "name": "stylus",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "body",
+                              "line": 5
+                            },
+                            {
+                              "type": "Text",
+                              "val": "\n",
+                              "line": 6
+                            },
+                            {
+                              "type": "Text",
+                              "val": "  padding: 50px",
+                              "line": 6
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "line": 4,
+                        "filename": "filters.stylus.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [
+                    {
+                      "name": "type",
+                      "val": "\"text/css\"",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "filters.stylus.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "filters.stylus.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 7,
+            "filename": "filters.stylus.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "filters.stylus.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "filters.stylus.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.stylus.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.stylus.tokens.json
new file mode 100644
index 0000000..c50bf8c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/filters.stylus.tokens.json
@@ -0,0 +1,18 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"style","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"\"text/css\"","escaped":true}]}
+{"type":"indent","line":4,"val":6}
+{"type":"filter","line":4,"val":"stylus"}
+{"type":"start-pipeless-text","line":4}
+{"type":"text","line":5,"val":"body"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"  padding: 50px"}
+{"type":"end-pipeless-text","line":6}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"body","selfClosing":false}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html.expected.json
new file mode 100644
index 0000000..ce60cab
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html.expected.json
@@ -0,0 +1,35 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Text",
+      "val": "<ul>\n<li>foo</li>\n<li>bar</li>\n<li>baz</li>\n</ul>",
+      "filename": "html.tokens.json",
+      "line": 2,
+      "isHtml": true
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "You can <em>embed</em> html as well.",
+            "line": 8,
+            "filename": "html.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 8,
+      "filename": "html.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "html.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html.tokens.json
new file mode 100644
index 0000000..e844031
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html.tokens.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"text-html","line":2,"val":"<ul>"}
+{"type":"indent","line":3,"val":2}
+{"type":"text-html","line":3,"val":"<li>foo</li>"}
+{"type":"newline","line":4}
+{"type":"text-html","line":4,"val":"<li>bar</li>"}
+{"type":"newline","line":5}
+{"type":"text-html","line":5,"val":"<li>baz</li>"}
+{"type":"outdent","line":6}
+{"type":"text-html","line":6,"val":"</ul>"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"You can <em>embed</em> html as well."}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html5.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html5.expected.json
new file mode 100644
index 0000000..1c4b4bb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html5.expected.json
@@ -0,0 +1,88 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Doctype",
+      "val": "html",
+      "line": 1,
+      "filename": "html5.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "input",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'checkbox'",
+          "escaped": true
+        },
+        {
+          "name": "checked",
+          "val": true,
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "html5.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "input",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'checkbox'",
+          "escaped": true
+        },
+        {
+          "name": "checked",
+          "val": "true",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "html5.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "input",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'checkbox'",
+          "escaped": true
+        },
+        {
+          "name": "checked",
+          "val": "false",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 4,
+      "filename": "html5.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "html5.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html5.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html5.tokens.json
new file mode 100644
index 0000000..fe0aec5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/html5.tokens.json
@@ -0,0 +1,11 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"input","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"type","val":"'checkbox'","escaped":true},{"name":"checked","val":true,"escaped":false}]}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"input","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"'checkbox'","escaped":true},{"name":"checked","val":"true","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"input","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"type","val":"'checkbox'","escaped":true},{"name":"checked","val":"false","escaped":true}]}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-from-root.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-from-root.expected.json
new file mode 100644
index 0000000..51920a1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-from-root.expected.json
@@ -0,0 +1,23 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "/auxiliary/extends-from-root",
+        "line": 1,
+        "filename": "include-extends-from-root.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "line": 1,
+      "filename": "include-extends-from-root.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-extends-from-root.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-from-root.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-from-root.tokens.json
new file mode 100644
index 0000000..d5d77cc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-from-root.tokens.json
@@ -0,0 +1,2 @@
+{"type":"include","line":1,"val":"/auxiliary/extends-from-root"}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-of-common-template.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-of-common-template.expected.json
new file mode 100644
index 0000000..f84f831
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-of-common-template.expected.json
@@ -0,0 +1,39 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/extends-empty-block-1",
+        "line": 1,
+        "filename": "include-extends-of-common-template.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "line": 1,
+      "filename": "include-extends-of-common-template.tokens.json"
+    },
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/extends-empty-block-2",
+        "line": 2,
+        "filename": "include-extends-of-common-template.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "line": 2,
+      "filename": "include-extends-of-common-template.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-extends-of-common-template.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-of-common-template.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-of-common-template.tokens.json
new file mode 100644
index 0000000..afd725e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-extends-of-common-template.tokens.json
@@ -0,0 +1,5 @@
+{"type":"include","line":1,"val":"auxiliary/extends-empty-block-1"}
+{"type":"newline","line":2}
+{"type":"include","line":2,"val":"auxiliary/extends-empty-block-2"}
+{"type":"newline","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter-stylus.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter-stylus.expected.json
new file mode 100644
index 0000000..3c7ec85
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter-stylus.expected.json
@@ -0,0 +1,45 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "style",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Include",
+            "file": {
+              "type": "FileReference",
+              "path": "some.styl",
+              "line": 2,
+              "filename": "include-filter-stylus.tokens.json"
+            },
+            "filter": "styl",
+            "attrs": [],
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "line": 2,
+            "filename": "include-filter-stylus.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "\"text/css\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "include-filter-stylus.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-filter-stylus.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter-stylus.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter-stylus.tokens.json
new file mode 100644
index 0000000..685fccd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter-stylus.tokens.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"style","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"\"text/css\"","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"include","line":2,"val":"some.styl","filter":"styl","attrs":null}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter.expected.json
new file mode 100644
index 0000000..5caa347
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter.expected.json
@@ -0,0 +1,130 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Include",
+                  "file": {
+                    "type": "FileReference",
+                    "path": "some.md",
+                    "line": 3,
+                    "filename": "include-filter.tokens.json"
+                  },
+                  "filter": "md",
+                  "attrs": [],
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "line": 3,
+                  "filename": "include-filter.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "script",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Include",
+                        "file": {
+                          "type": "FileReference",
+                          "path": "include-filter-coffee.coffee",
+                          "line": 5,
+                          "filename": "include-filter.tokens.json"
+                        },
+                        "filter": "coffee",
+                        "attrs": [
+                          {
+                            "name": "minify",
+                            "val": "true",
+                            "escaped": true
+                          }
+                        ],
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "line": 5,
+                        "filename": "include-filter.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "include-filter.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "script",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Include",
+                        "file": {
+                          "type": "FileReference",
+                          "path": "include-filter-coffee.coffee",
+                          "line": 7,
+                          "filename": "include-filter.tokens.json"
+                        },
+                        "filter": "coffee",
+                        "attrs": [
+                          {
+                            "name": "minify",
+                            "val": "false",
+                            "escaped": true
+                          }
+                        ],
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "line": 7,
+                        "filename": "include-filter.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "include-filter.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "include-filter.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "include-filter.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-filter.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter.tokens.json
new file mode 100644
index 0000000..1568886
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-filter.tokens.json
@@ -0,0 +1,17 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"some.md","filter":"md","attrs":null}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"indent","line":5,"val":6}
+{"type":"include","line":5,"val":"include-filter-coffee.coffee","filter":"coffee","attrs":{"type":"attrs","line":5,"attrs":[{"name":"minify","val":"true","escaped":true}]}}
+{"type":"outdent","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"indent","line":7,"val":6}
+{"type":"include","line":7,"val":"include-filter-coffee.coffee","filter":"coffee","attrs":{"type":"attrs","line":7,"attrs":[{"name":"minify","val":"false","escaped":true}]}}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text-body.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text-body.expected.json
new file mode 100644
index 0000000..b58f350
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text-body.expected.json
@@ -0,0 +1,26 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Text",
+      "val": "The message is \"",
+      "line": 1,
+      "filename": "include-only-text-body.tokens.json"
+    },
+    {
+      "type": "Block",
+      "nodes": [],
+      "line": 2,
+      "filename": "include-only-text-body.tokens.json",
+      "yield": true
+    },
+    {
+      "type": "Text",
+      "val": "\"",
+      "line": 3,
+      "filename": "include-only-text-body.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-only-text-body.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text-body.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text-body.tokens.json
new file mode 100644
index 0000000..0d8426f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text-body.tokens.json
@@ -0,0 +1,7 @@
+{"type":"text","line":1,"val":"The message is \""}
+{"type":"newline","line":2}
+{"type":"yield","line":2}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"\""}
+{"type":"newline","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text.expected.json
new file mode 100644
index 0000000..a43bcf3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text.expected.json
@@ -0,0 +1,92 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Include",
+                        "file": {
+                          "type": "FileReference",
+                          "path": "include-only-text-body",
+                          "line": 4,
+                          "filename": "include-only-text.tokens.json"
+                        },
+                        "attrs": [],
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "em",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "hello world",
+                                    "line": 5,
+                                    "filename": "include-only-text.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [],
+                              "attributeBlocks": [],
+                              "isInline": true,
+                              "line": 5,
+                              "filename": "include-only-text.tokens.json"
+                            }
+                          ],
+                          "line": 5,
+                          "filename": "include-only-text.tokens.json"
+                        },
+                        "line": 4,
+                        "filename": "include-only-text.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "include-only-text.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "include-only-text.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "include-only-text.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-only-text.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text.tokens.json
new file mode 100644
index 0000000..f5a07e7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-only-text.tokens.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"indent","line":4,"val":6}
+{"type":"include","line":4,"val":"include-only-text-body"}
+{"type":"indent","line":5,"val":8}
+{"type":"tag","line":5,"val":"em","selfClosing":false}
+{"type":"text","line":5,"val":"hello world"}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text-head.expected.json
new file mode 100644
index 0000000..3d0d940
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text-head.expected.json
@@ -0,0 +1,49 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "head",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "alert('hello world');",
+                  "line": 3
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "type",
+                "val": "'text/javascript'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "include-with-text-head.tokens.json",
+            "textOnly": true
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "include-with-text-head.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-with-text-head.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text-head.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text-head.tokens.json
new file mode 100644
index 0000000..19e2e4d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text-head.tokens.json
@@ -0,0 +1,10 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"script","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"type","val":"'text/javascript'","escaped":true}]}
+{"type":"dot","line":2}
+{"type":"start-pipeless-text","line":2}
+{"type":"text","line":3,"val":"alert('hello world');"}
+{"type":"end-pipeless-text","line":3}
+{"type":"outdent","line":3}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text.expected.json
new file mode 100644
index 0000000..e6038ad
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text.expected.json
@@ -0,0 +1,81 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Include",
+            "file": {
+              "type": "FileReference",
+              "path": "include-with-text-head",
+              "line": 2,
+              "filename": "include-with-text.tokens.json"
+            },
+            "attrs": [],
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "script",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "src",
+                      "val": "'/caustic.js'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "include-with-text.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "script",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "src",
+                      "val": "'/app.js'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "include-with-text.tokens.json"
+                }
+              ],
+              "line": 3,
+              "filename": "include-with-text.tokens.json"
+            },
+            "line": 2,
+            "filename": "include-with-text.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "include-with-text.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include-with-text.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text.tokens.json
new file mode 100644
index 0000000..2bb5bd9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include-with-text.tokens.json
@@ -0,0 +1,12 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"include","line":2,"val":"include-with-text-head"}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"script","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"src","val":"'/caustic.js'","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/app.js'","escaped":true}]}
+{"type":"outdent","line":5}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.script.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.script.expected.json
new file mode 100644
index 0000000..cf9f334
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.script.expected.json
@@ -0,0 +1,49 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Include",
+            "file": {
+              "type": "FileReference",
+              "path": "auxiliary/pet",
+              "line": 2,
+              "filename": "include.script.tokens.json"
+            },
+            "attrs": [],
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "line": 2,
+            "filename": "include.script.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "id",
+          "val": "'pet-template'",
+          "escaped": false
+        },
+        {
+          "name": "type",
+          "val": "'text/x-template'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "include.script.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include.script.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.script.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.script.tokens.json
new file mode 100644
index 0000000..2355940
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.script.tokens.json
@@ -0,0 +1,7 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"id","line":1,"val":"pet-template"}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"include","line":2,"val":"auxiliary/pet"}
+{"type":"outdent","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.yield.nested.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.yield.nested.expected.json
new file mode 100644
index 0000000..fe7dd08
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.yield.nested.expected.json
@@ -0,0 +1,68 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/yield-nested",
+        "line": 2,
+        "filename": "include.yield.nested.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "some content",
+                  "line": 3,
+                  "filename": "include.yield.nested.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "include.yield.nested.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "and some more",
+                  "line": 4,
+                  "filename": "include.yield.nested.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "include.yield.nested.tokens.json"
+          }
+        ],
+        "line": 3,
+        "filename": "include.yield.nested.tokens.json"
+      },
+      "line": 2,
+      "filename": "include.yield.nested.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "include.yield.nested.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.yield.nested.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.yield.nested.tokens.json
new file mode 100644
index 0000000..efaf5ac
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/include.yield.nested.tokens.json
@@ -0,0 +1,10 @@
+{"type":"newline","line":2}
+{"type":"include","line":2,"val":"auxiliary/yield-nested"}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"text","line":3,"val":"some content"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"and some more"}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes-with-ext-js.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes-with-ext-js.expected.json
new file mode 100644
index 0000000..c55cf2e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes-with-ext-js.expected.json
@@ -0,0 +1,53 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "pre",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "code",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Include",
+                  "file": {
+                    "type": "FileReference",
+                    "path": "javascript-new-lines.js",
+                    "line": 3,
+                    "filename": "includes-with-ext-js.tokens.json"
+                  },
+                  "attrs": [],
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "line": 3,
+                  "filename": "includes-with-ext-js.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": true,
+            "line": 2,
+            "filename": "includes-with-ext-js.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "includes-with-ext-js.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "includes-with-ext-js.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes-with-ext-js.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes-with-ext-js.tokens.json
new file mode 100644
index 0000000..239ec2c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes-with-ext-js.tokens.json
@@ -0,0 +1,8 @@
+{"type":"tag","line":1,"val":"pre","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"code","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"javascript-new-lines.js"}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes.expected.json
new file mode 100644
index 0000000..d8ed277
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes.expected.json
@@ -0,0 +1,118 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/mixins",
+        "line": 2,
+        "filename": "includes.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "line": 2,
+      "filename": "includes.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "block": null,
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 4,
+      "filename": "includes.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "body",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Include",
+            "file": {
+              "type": "FileReference",
+              "path": "auxiliary/smile.html",
+              "line": 7,
+              "filename": "includes.tokens.json"
+            },
+            "attrs": [],
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "line": 7,
+            "filename": "includes.tokens.json"
+          },
+          {
+            "type": "Include",
+            "file": {
+              "type": "FileReference",
+              "path": "auxiliary/escapes.html",
+              "line": 8,
+              "filename": "includes.tokens.json"
+            },
+            "attrs": [],
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "line": 8,
+            "filename": "includes.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Include",
+                  "file": {
+                    "type": "FileReference",
+                    "path": "auxiliary/includable.js",
+                    "line": 10,
+                    "filename": "includes.tokens.json"
+                  },
+                  "filter": "js",
+                  "attrs": [],
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "line": 10,
+                  "filename": "includes.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "type",
+                "val": "\"text/javascript\"",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 9,
+            "filename": "includes.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "includes.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "includes.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes.tokens.json
new file mode 100644
index 0000000..6857e59
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/includes.tokens.json
@@ -0,0 +1,18 @@
+{"type":"newline","line":2}
+{"type":"include","line":2,"val":"auxiliary/mixins"}
+{"type":"newline","line":4}
+{"type":"call","line":4,"val":"foo"}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"body","selfClosing":false}
+{"type":"indent","line":7,"val":2}
+{"type":"include","line":7,"val":"auxiliary/smile.html"}
+{"type":"newline","line":8}
+{"type":"include","line":8,"val":"auxiliary/escapes.html"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"script","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"type","val":"\"text/javascript\"","escaped":true}]}
+{"type":"indent","line":10,"val":4}
+{"type":"include","line":10,"val":"auxiliary/includable.js","filter":"js","attrs":null}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"eos","line":11}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.alert-dialog.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.alert-dialog.expected.json
new file mode 100644
index 0000000..f2d0692
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.alert-dialog.expected.json
@@ -0,0 +1,69 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/dialog",
+        "line": 2,
+        "filename": "inheritance.alert-dialog.tokens.json"
+      },
+      "line": 2,
+      "filename": "inheritance.alert-dialog.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h1",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Alert!",
+                "line": 5,
+                "filename": "inheritance.alert-dialog.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "inheritance.alert-dialog.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "I'm an alert!",
+                "line": 6,
+                "filename": "inheritance.alert-dialog.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 6,
+          "filename": "inheritance.alert-dialog.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "inheritance.alert-dialog.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.alert-dialog.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.alert-dialog.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.alert-dialog.tokens.json
new file mode 100644
index 0000000..6ae9d78
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.alert-dialog.tokens.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/dialog"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"content","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"h1","selfClosing":false}
+{"type":"text","line":5,"val":"Alert!"}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"p","selfClosing":false}
+{"type":"text","line":6,"val":"I'm an alert!"}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.defaults.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.defaults.expected.json
new file mode 100644
index 0000000..8b04974
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.defaults.expected.json
@@ -0,0 +1,106 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "head",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "NamedBlock",
+                  "nodes": [
+                    {
+                      "type": "Tag",
+                      "name": "script",
+                      "selfClosing": false,
+                      "block": {
+                        "type": "Block",
+                        "nodes": []
+                      },
+                      "attrs": [
+                        {
+                          "name": "src",
+                          "val": "'jquery.js'",
+                          "escaped": true
+                        }
+                      ],
+                      "attributeBlocks": [],
+                      "isInline": false,
+                      "line": 4,
+                      "filename": "inheritance.defaults.tokens.json"
+                    },
+                    {
+                      "type": "Tag",
+                      "name": "script",
+                      "selfClosing": false,
+                      "block": {
+                        "type": "Block",
+                        "nodes": []
+                      },
+                      "attrs": [
+                        {
+                          "name": "src",
+                          "val": "'keymaster.js'",
+                          "escaped": true
+                        }
+                      ],
+                      "attributeBlocks": [],
+                      "isInline": false,
+                      "line": 5,
+                      "filename": "inheritance.defaults.tokens.json"
+                    },
+                    {
+                      "type": "Tag",
+                      "name": "script",
+                      "selfClosing": false,
+                      "block": {
+                        "type": "Block",
+                        "nodes": []
+                      },
+                      "attrs": [
+                        {
+                          "name": "src",
+                          "val": "'caustic.js'",
+                          "escaped": true
+                        }
+                      ],
+                      "attributeBlocks": [],
+                      "isInline": false,
+                      "line": 6,
+                      "filename": "inheritance.defaults.tokens.json"
+                    }
+                  ],
+                  "line": 3,
+                  "filename": "inheritance.defaults.tokens.json",
+                  "name": "head",
+                  "mode": "replace"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "inheritance.defaults.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "inheritance.defaults.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.defaults.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.defaults.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.defaults.tokens.json
new file mode 100644
index 0000000..dc94bf2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.defaults.tokens.json
@@ -0,0 +1,18 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"block","line":3,"val":"head","mode":"replace"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'keymaster.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'caustic.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.expected.json
new file mode 100644
index 0000000..3151b26
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.expected.json
@@ -0,0 +1,98 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/layout",
+        "line": 2,
+        "filename": "inheritance.tokens.json"
+      },
+      "line": 2,
+      "filename": "inheritance.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'jquery.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "inheritance.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "inheritance.tokens.json",
+      "name": "head",
+      "mode": "replace"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h2",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Page",
+                "line": 8,
+                "filename": "inheritance.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 8,
+          "filename": "inheritance.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Some content",
+                "line": 9,
+                "filename": "inheritance.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 9,
+          "filename": "inheritance.tokens.json"
+        }
+      ],
+      "line": 7,
+      "filename": "inheritance.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.expected.json
new file mode 100644
index 0000000..d33e568
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.expected.json
@@ -0,0 +1,98 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/layout",
+        "line": 2,
+        "filename": "inheritance.extend.tokens.json"
+      },
+      "line": 2,
+      "filename": "inheritance.extend.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'jquery.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "inheritance.extend.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "inheritance.extend.tokens.json",
+      "name": "head",
+      "mode": "replace"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h2",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Page",
+                "line": 8,
+                "filename": "inheritance.extend.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 8,
+          "filename": "inheritance.extend.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Some content",
+                "line": 9,
+                "filename": "inheritance.extend.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 9,
+          "filename": "inheritance.extend.tokens.json"
+        }
+      ],
+      "line": 7,
+      "filename": "inheritance.extend.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.extend.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.include.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.include.expected.json
new file mode 100644
index 0000000..43ea527
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.include.expected.json
@@ -0,0 +1,149 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/layout.include",
+        "line": 2,
+        "filename": "inheritance.extend.include.tokens.json"
+      },
+      "line": 2,
+      "filename": "inheritance.extend.include.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'jquery.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "inheritance.extend.include.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "inheritance.extend.include.tokens.json",
+      "name": "head",
+      "mode": "replace"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h2",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Page",
+                "line": 8,
+                "filename": "inheritance.extend.include.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 8,
+          "filename": "inheritance.extend.include.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Some content",
+                "line": 9,
+                "filename": "inheritance.extend.include.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 9,
+          "filename": "inheritance.extend.include.tokens.json"
+        }
+      ],
+      "line": 7,
+      "filename": "inheritance.extend.include.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h2",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Awesome",
+                "line": 12,
+                "filename": "inheritance.extend.include.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 12,
+          "filename": "inheritance.extend.include.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Now we can extend included blocks!",
+                "line": 13,
+                "filename": "inheritance.extend.include.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 13,
+          "filename": "inheritance.extend.include.tokens.json"
+        }
+      ],
+      "line": 11,
+      "filename": "inheritance.extend.include.tokens.json",
+      "name": "window-content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.extend.include.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.include.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.include.tokens.json
new file mode 100644
index 0000000..e65f218
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.include.tokens.json
@@ -0,0 +1,25 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout.include"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"block","line":7,"val":"content","mode":"replace"}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"h2","selfClosing":false}
+{"type":"text","line":8,"val":"Page"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"Some content"}
+{"type":"outdent","line":11}
+{"type":"block","line":11,"val":"window-content","mode":"replace"}
+{"type":"indent","line":12,"val":2}
+{"type":"tag","line":12,"val":"h2","selfClosing":false}
+{"type":"text","line":12,"val":"Awesome"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"text","line":13,"val":"Now we can extend included blocks!"}
+{"type":"outdent","line":13}
+{"type":"eos","line":13}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.block.expected.json
new file mode 100644
index 0000000..e9ce57e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.block.expected.json
@@ -0,0 +1,48 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/inheritance.extend.mixin.block",
+        "line": 1,
+        "filename": "inheritance.extend.mixins.block.tokens.json"
+      },
+      "line": 1,
+      "filename": "inheritance.extend.mixins.block.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Hello World!",
+                "line": 4,
+                "filename": "inheritance.extend.mixins.block.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 4,
+          "filename": "inheritance.extend.mixins.block.tokens.json"
+        }
+      ],
+      "line": 3,
+      "filename": "inheritance.extend.mixins.block.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.extend.mixins.block.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.block.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.block.tokens.json
new file mode 100644
index 0000000..7405423
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.block.tokens.json
@@ -0,0 +1,8 @@
+{"type":"extends","line":1,"val":"auxiliary/inheritance.extend.mixin.block"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"content","mode":"replace"}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"Hello World!"}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.expected.json
new file mode 100644
index 0000000..bd34c3f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.expected.json
@@ -0,0 +1,120 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/layout",
+        "line": 2,
+        "filename": "inheritance.extend.mixins.tokens.json"
+      },
+      "line": 2,
+      "filename": "inheritance.extend.mixins.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "article",
+      "args": "title",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Code",
+            "val": "if ( title)",
+            "line": 5,
+            "filename": "inheritance.extend.mixins.tokens.json",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "inheritance.extend.mixins.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "title",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 6,
+                    "filename": "inheritance.extend.mixins.tokens.json"
+                  }
+                }
+              ],
+              "line": 6,
+              "filename": "inheritance.extend.mixins.tokens.json"
+            }
+          },
+          {
+            "type": "MixinBlock",
+            "line": 7,
+            "filename": "inheritance.extend.mixins.tokens.json"
+          }
+        ],
+        "line": 5,
+        "filename": "inheritance.extend.mixins.tokens.json"
+      },
+      "call": false,
+      "line": 4,
+      "filename": "inheritance.extend.mixins.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Mixin",
+          "name": "article",
+          "args": "\"The meaning of life\"",
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Tag",
+                "name": "p",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": [
+                    {
+                      "type": "Text",
+                      "val": "Foo bar baz!",
+                      "line": 11,
+                      "filename": "inheritance.extend.mixins.tokens.json"
+                    }
+                  ]
+                },
+                "attrs": [],
+                "attributeBlocks": [],
+                "isInline": false,
+                "line": 11,
+                "filename": "inheritance.extend.mixins.tokens.json"
+              }
+            ],
+            "line": 10,
+            "filename": "inheritance.extend.mixins.tokens.json"
+          },
+          "call": true,
+          "attrs": [],
+          "attributeBlocks": [],
+          "line": 10,
+          "filename": "inheritance.extend.mixins.tokens.json"
+        }
+      ],
+      "line": 9,
+      "filename": "inheritance.extend.mixins.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.extend.mixins.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.tokens.json
new file mode 100644
index 0000000..eaea287
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.mixins.tokens.json
@@ -0,0 +1,21 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"mixin","line":4,"val":"article","args":"title"}
+{"type":"indent","line":5,"val":2}
+{"type":"code","line":5,"val":"if ( title)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":6,"val":4}
+{"type":"tag","line":6,"val":"h1","selfClosing":false}
+{"type":"code","line":6,"val":"title","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"mixin-block","line":7}
+{"type":"outdent","line":9}
+{"type":"block","line":9,"val":"content","mode":"replace"}
+{"type":"indent","line":10,"val":2}
+{"type":"call","line":10,"val":"article","args":"\"The meaning of life\""}
+{"type":"indent","line":11,"val":4}
+{"type":"tag","line":11,"val":"p","selfClosing":false}
+{"type":"text","line":11,"val":"Foo bar baz!"}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"eos","line":11}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.recursive.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.recursive.expected.json
new file mode 100644
index 0000000..4de437d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.recursive.expected.json
@@ -0,0 +1,48 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "/auxiliary/inheritance.extend.recursive-parent.jade",
+        "line": 1,
+        "filename": "inheritance.extend.recursive.tokens.json"
+      },
+      "line": 1,
+      "filename": "inheritance.extend.recursive.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h4",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "child",
+                "line": 4,
+                "filename": "inheritance.extend.recursive.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 4,
+          "filename": "inheritance.extend.recursive.tokens.json"
+        }
+      ],
+      "line": 3,
+      "filename": "inheritance.extend.recursive.tokens.json",
+      "name": "parent",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.extend.recursive.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.recursive.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.recursive.tokens.json
new file mode 100644
index 0000000..0033858
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.recursive.tokens.json
@@ -0,0 +1,8 @@
+{"type":"extends","line":1,"val":"/auxiliary/inheritance.extend.recursive-parent.jade"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"parent","mode":"replace"}
+{"type":"indent","line":4,"val":4}
+{"type":"tag","line":4,"val":"h4","selfClosing":false}
+{"type":"text","line":4,"val":"child"}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.tokens.json
new file mode 100644
index 0000000..f2f3354
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.tokens.json
@@ -0,0 +1,17 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"block","line":7,"val":"content","mode":"replace"}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"h2","selfClosing":false}
+{"type":"text","line":8,"val":"Page"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"Some content"}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.whitespace.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.whitespace.expected.json
new file mode 100644
index 0000000..7b97949
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.whitespace.expected.json
@@ -0,0 +1,98 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "auxiliary/layout",
+        "line": 2,
+        "filename": "inheritance.extend.whitespace.tokens.json"
+      },
+      "line": 2,
+      "filename": "inheritance.extend.whitespace.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'jquery.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 6,
+          "filename": "inheritance.extend.whitespace.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "inheritance.extend.whitespace.tokens.json",
+      "name": "head",
+      "mode": "replace"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "h2",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Page",
+                "line": 12,
+                "filename": "inheritance.extend.whitespace.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 12,
+          "filename": "inheritance.extend.whitespace.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Some content",
+                "line": 13,
+                "filename": "inheritance.extend.whitespace.tokens.json"
+              }
+            ]
+          },
+          "attrs": [],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 13,
+          "filename": "inheritance.extend.whitespace.tokens.json"
+        }
+      ],
+      "line": 8,
+      "filename": "inheritance.extend.whitespace.tokens.json",
+      "name": "content",
+      "mode": "replace"
+    }
+  ],
+  "line": 0,
+  "filename": "inheritance.extend.whitespace.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.whitespace.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.whitespace.tokens.json
new file mode 100644
index 0000000..753128a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.extend.whitespace.tokens.json
@@ -0,0 +1,17 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":6,"val":2}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":8}
+{"type":"block","line":8,"val":"content","mode":"replace"}
+{"type":"indent","line":12,"val":2}
+{"type":"tag","line":12,"val":"h2","selfClosing":false}
+{"type":"text","line":12,"val":"Page"}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"text","line":13,"val":"Some content"}
+{"type":"outdent","line":13}
+{"type":"eos","line":13}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.tokens.json
new file mode 100644
index 0000000..f2f3354
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inheritance.tokens.json
@@ -0,0 +1,17 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"auxiliary/layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"replace"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"block","line":7,"val":"content","mode":"replace"}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"h2","selfClosing":false}
+{"type":"text","line":8,"val":"Page"}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"Some content"}
+{"type":"outdent","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inline-tag.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inline-tag.expected.json
new file mode 100644
index 0000000..49778c0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inline-tag.expected.json
@@ -0,0 +1,275 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "bing ",
+                "line": 1,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Tag",
+                "name": "strong",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": [
+                    {
+                      "type": "Text",
+                      "val": "foo",
+                      "line": 1,
+                      "filename": "inline-tag.tokens.json"
+                    }
+                  ]
+                },
+                "attrs": [],
+                "attributeBlocks": [],
+                "isInline": true,
+                "line": 1,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": " bong",
+                "line": 1,
+                "filename": "inline-tag.tokens.json"
+              }
+            ],
+            "line": 1,
+            "filename": "inline-tag.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "inline-tag.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "bing",
+            "line": 4
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 5
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 5
+          },
+          {
+            "type": "Tag",
+            "name": "strong",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 1,
+                  "filename": "inline-tag.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": true,
+            "line": 1,
+            "filename": "inline-tag.tokens.json"
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 5
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 6
+          },
+          {
+            "type": "Text",
+            "val": "bong",
+            "line": 6
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 7
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 7
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "inline-tag.tokens.json",
+      "textOnly": true
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "bing",
+                "line": 9,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 10,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "",
+                "line": 10,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Tag",
+                "name": "strong",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": [
+                    {
+                      "type": "Text",
+                      "val": "foo",
+                      "line": 1,
+                      "filename": "inline-tag.tokens.json"
+                    }
+                  ]
+                },
+                "attrs": [],
+                "attributeBlocks": [],
+                "isInline": true,
+                "line": 1,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "",
+                "line": 10,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 11,
+                "filename": "inline-tag.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "bong",
+                "line": 11,
+                "filename": "inline-tag.tokens.json"
+              }
+            ],
+            "line": 9,
+            "filename": "inline-tag.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 8,
+      "filename": "inline-tag.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "#[strong escaped]",
+            "line": 14
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 15
+          },
+          {
+            "type": "Text",
+            "val": "#[",
+            "line": 15
+          },
+          {
+            "type": "Tag",
+            "name": "strong",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "escaped",
+                  "line": 1,
+                  "filename": "inline-tag.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": true,
+            "line": 1,
+            "filename": "inline-tag.tokens.json"
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 15
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 13,
+      "filename": "inline-tag.tokens.json",
+      "textOnly": true
+    }
+  ],
+  "line": 0,
+  "filename": "inline-tag.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inline-tag.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inline-tag.tokens.json
new file mode 100644
index 0000000..466de23
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/inline-tag.tokens.json
@@ -0,0 +1,51 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"bing "}
+{"type":"start-jade-interpolation","line":1}
+{"type":"tag","line":1,"val":"strong","selfClosing":false}
+{"type":"text","line":1,"val":"foo"}
+{"type":"end-jade-interpolation","line":1}
+{"type":"text","line":1,"val":" bong"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"dot","line":3}
+{"type":"start-pipeless-text","line":3}
+{"type":"text","line":4,"val":"bing"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"start-jade-interpolation","line":5}
+{"type":"tag","line":1,"val":"strong","selfClosing":false}
+{"type":"text","line":1,"val":"foo"}
+{"type":"end-jade-interpolation","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"bong"}
+{"type":"newline","line":7}
+{"type":"text","line":7,"val":""}
+{"type":"end-pipeless-text","line":7}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"indent","line":9,"val":2}
+{"type":"text","line":9,"val":"bing"}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":""}
+{"type":"start-jade-interpolation","line":10}
+{"type":"tag","line":1,"val":"strong","selfClosing":false}
+{"type":"text","line":1,"val":"foo"}
+{"type":"end-jade-interpolation","line":10}
+{"type":"text","line":10,"val":""}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"bong"}
+{"type":"outdent","line":13}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"dot","line":13}
+{"type":"start-pipeless-text","line":13}
+{"type":"text","line":14,"val":"#[strong escaped]"}
+{"type":"newline","line":15}
+{"type":"text","line":15,"val":"#["}
+{"type":"start-jade-interpolation","line":15}
+{"type":"tag","line":1,"val":"strong","selfClosing":false}
+{"type":"text","line":1,"val":"escaped"}
+{"type":"end-jade-interpolation","line":15}
+{"type":"text","line":15,"val":""}
+{"type":"end-pipeless-text","line":15}
+{"type":"eos","line":15}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/intepolated-elements.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/intepolated-elements.expected.json
new file mode 100644
index 0000000..d9b356d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/intepolated-elements.expected.json
@@ -0,0 +1,213 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "",
+                "line": 1,
+                "filename": "intepolated-elements.tokens.json"
+              },
+              {
+                "type": "Tag",
+                "name": "a",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": [
+                    {
+                      "type": "Text",
+                      "val": "with inline link",
+                      "line": 1,
+                      "filename": "intepolated-elements.tokens.json"
+                    }
+                  ]
+                },
+                "attrs": [
+                  {
+                    "name": "class",
+                    "val": "'rho'",
+                    "escaped": false
+                  },
+                  {
+                    "name": "href",
+                    "val": "'#'",
+                    "escaped": true
+                  },
+                  {
+                    "name": "class",
+                    "val": "'rho--modifier'",
+                    "escaped": true
+                  }
+                ],
+                "attributeBlocks": [],
+                "isInline": true,
+                "line": 1,
+                "filename": "intepolated-elements.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "",
+                "line": 1,
+                "filename": "intepolated-elements.tokens.json"
+              }
+            ],
+            "line": 1,
+            "filename": "intepolated-elements.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "intepolated-elements.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Some text ",
+                "line": 2,
+                "filename": "intepolated-elements.tokens.json"
+              },
+              {
+                "type": "Tag",
+                "name": "a",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": []
+                },
+                "attrs": [
+                  {
+                    "name": "class",
+                    "val": "'rho'",
+                    "escaped": false
+                  },
+                  {
+                    "name": "href",
+                    "val": "'#'",
+                    "escaped": true
+                  },
+                  {
+                    "name": "class",
+                    "val": "'rho--modifier'",
+                    "escaped": true
+                  }
+                ],
+                "attributeBlocks": [],
+                "isInline": true,
+                "line": 1,
+                "filename": "intepolated-elements.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "",
+                "line": 2,
+                "filename": "intepolated-elements.tokens.json"
+              }
+            ],
+            "line": 2,
+            "filename": "intepolated-elements.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "intepolated-elements.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Some text ",
+                "line": 3,
+                "filename": "intepolated-elements.tokens.json"
+              },
+              {
+                "type": "Tag",
+                "name": "a",
+                "selfClosing": false,
+                "block": {
+                  "type": "Block",
+                  "nodes": [
+                    {
+                      "type": "Text",
+                      "val": "with inline link",
+                      "line": 1,
+                      "filename": "intepolated-elements.tokens.json"
+                    }
+                  ]
+                },
+                "attrs": [
+                  {
+                    "name": "class",
+                    "val": "'rho'",
+                    "escaped": false
+                  },
+                  {
+                    "name": "href",
+                    "val": "'#'",
+                    "escaped": true
+                  },
+                  {
+                    "name": "class",
+                    "val": "'rho--modifier'",
+                    "escaped": true
+                  }
+                ],
+                "attributeBlocks": [],
+                "isInline": true,
+                "line": 1,
+                "filename": "intepolated-elements.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "",
+                "line": 3,
+                "filename": "intepolated-elements.tokens.json"
+              }
+            ],
+            "line": 3,
+            "filename": "intepolated-elements.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "intepolated-elements.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "intepolated-elements.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/intepolated-elements.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/intepolated-elements.tokens.json
new file mode 100644
index 0000000..fd38483
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/intepolated-elements.tokens.json
@@ -0,0 +1,29 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":""}
+{"type":"start-jade-interpolation","line":1}
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"class","line":1,"val":"rho"}
+{"type":"attrs","line":1,"attrs":[{"name":"href","val":"'#'","escaped":true},{"name":"class","val":"'rho--modifier'","escaped":true}]}
+{"type":"text","line":1,"val":"with inline link"}
+{"type":"end-jade-interpolation","line":1}
+{"type":"text","line":1,"val":""}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"text","line":2,"val":"Some text "}
+{"type":"start-jade-interpolation","line":2}
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"class","line":1,"val":"rho"}
+{"type":"attrs","line":1,"attrs":[{"name":"href","val":"'#'","escaped":true},{"name":"class","val":"'rho--modifier'","escaped":true}]}
+{"type":"end-jade-interpolation","line":2}
+{"type":"text","line":2,"val":""}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"text","line":3,"val":"Some text "}
+{"type":"start-jade-interpolation","line":3}
+{"type":"tag","line":1,"val":"a","selfClosing":false}
+{"type":"class","line":1,"val":"rho"}
+{"type":"attrs","line":1,"attrs":[{"name":"href","val":"'#'","escaped":true},{"name":"class","val":"'rho--modifier'","escaped":true}]}
+{"type":"text","line":1,"val":"with inline link"}
+{"type":"end-jade-interpolation","line":3}
+{"type":"text","line":3,"val":""}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/interpolation.escape.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/interpolation.escape.expected.json
new file mode 100644
index 0000000..e213587
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/interpolation.escape.expected.json
@@ -0,0 +1,79 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var id = 42;",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "interpolation.escape.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "some",
+                "line": 4,
+                "filename": "interpolation.escape.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 5,
+                "filename": "interpolation.escape.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\\#{text}",
+                "line": 5,
+                "filename": "interpolation.escape.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 6,
+                "filename": "interpolation.escape.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "here",
+                "line": 6,
+                "filename": "interpolation.escape.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 7,
+                "filename": "interpolation.escape.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "My ID #{\"is {\" + id + \"}\"}",
+                "line": 7,
+                "filename": "interpolation.escape.tokens.json"
+              }
+            ],
+            "line": 4,
+            "filename": "interpolation.escape.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "interpolation.escape.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "interpolation.escape.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/interpolation.escape.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/interpolation.escape.tokens.json
new file mode 100644
index 0000000..d343c12
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/interpolation.escape.tokens.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var id = 42;","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"text","line":4,"val":"some"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"\\#{text}"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"here"}
+{"type":"newline","line":7}
+{"type":"text","line":7,"val":"My ID #{\"is {\" + id + \"}\"}"}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.expected.json
new file mode 100644
index 0000000..69074fc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.expected.json
@@ -0,0 +1,67 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "../fixtures/append/app-layout",
+        "line": 2,
+        "filename": "layout.append.tokens.json"
+      },
+      "line": 2,
+      "filename": "layout.append.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'foo.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "layout.append.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'bar.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 6,
+          "filename": "layout.append.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "layout.append.tokens.json",
+      "name": "head",
+      "mode": "append"
+    }
+  ],
+  "line": 0,
+  "filename": "layout.append.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.tokens.json
new file mode 100644
index 0000000..eaaea3e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.tokens.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/append/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"append"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.without-block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.without-block.expected.json
new file mode 100644
index 0000000..5a202fd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.without-block.expected.json
@@ -0,0 +1,67 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "../fixtures/append-without-block/app-layout",
+        "line": 2,
+        "filename": "layout.append.without-block.tokens.json"
+      },
+      "line": 2,
+      "filename": "layout.append.without-block.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'foo.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "layout.append.without-block.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'bar.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 6,
+          "filename": "layout.append.without-block.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "layout.append.without-block.tokens.json",
+      "name": "head",
+      "mode": "append"
+    }
+  ],
+  "line": 0,
+  "filename": "layout.append.without-block.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.without-block.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.without-block.tokens.json
new file mode 100644
index 0000000..1857b30
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.append.without-block.tokens.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/append-without-block/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"append"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.multi.append.prepend.block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.multi.append.prepend.block.expected.json
new file mode 100644
index 0000000..9a857ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.multi.append.prepend.block.expected.json
@@ -0,0 +1,240 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "../fixtures/multi-append-prepend-block/redefine",
+        "line": 1,
+        "filename": "layout.multi.append.prepend.block.tokens.json"
+      },
+      "line": 1,
+      "filename": "layout.multi.append.prepend.block.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Something appended to content",
+                "line": 4,
+                "filename": "layout.multi.append.prepend.block.tokens.json"
+              }
+            ]
+          },
+          "attrs": [
+            {
+              "name": "class",
+              "val": "'first'",
+              "escaped": false
+            },
+            {
+              "name": "class",
+              "val": "'append'",
+              "escaped": false
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 4,
+          "filename": "layout.multi.append.prepend.block.tokens.json"
+        }
+      ],
+      "line": 3,
+      "filename": "layout.multi.append.prepend.block.tokens.json",
+      "name": "content",
+      "mode": "append"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Something prepended to content",
+                "line": 7,
+                "filename": "layout.multi.append.prepend.block.tokens.json"
+              }
+            ]
+          },
+          "attrs": [
+            {
+              "name": "class",
+              "val": "'first'",
+              "escaped": false
+            },
+            {
+              "name": "class",
+              "val": "'prepend'",
+              "escaped": false
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 7,
+          "filename": "layout.multi.append.prepend.block.tokens.json"
+        }
+      ],
+      "line": 6,
+      "filename": "layout.multi.append.prepend.block.tokens.json",
+      "name": "content",
+      "mode": "prepend"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Last append must be most last",
+                "line": 10,
+                "filename": "layout.multi.append.prepend.block.tokens.json"
+              }
+            ]
+          },
+          "attrs": [
+            {
+              "name": "class",
+              "val": "'last'",
+              "escaped": false
+            },
+            {
+              "name": "class",
+              "val": "'append'",
+              "escaped": false
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 10,
+          "filename": "layout.multi.append.prepend.block.tokens.json"
+        }
+      ],
+      "line": 9,
+      "filename": "layout.multi.append.prepend.block.tokens.json",
+      "name": "content",
+      "mode": "append"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "p",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "Last prepend must appear at top",
+                "line": 13,
+                "filename": "layout.multi.append.prepend.block.tokens.json"
+              }
+            ]
+          },
+          "attrs": [
+            {
+              "name": "class",
+              "val": "'first'",
+              "escaped": false
+            },
+            {
+              "name": "class",
+              "val": "'prepend'",
+              "escaped": false
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 13,
+          "filename": "layout.multi.append.prepend.block.tokens.json"
+        }
+      ],
+      "line": 12,
+      "filename": "layout.multi.append.prepend.block.tokens.json",
+      "name": "content",
+      "mode": "prepend"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'jquery.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 16,
+          "filename": "layout.multi.append.prepend.block.tokens.json"
+        }
+      ],
+      "line": 15,
+      "filename": "layout.multi.append.prepend.block.tokens.json",
+      "name": "head",
+      "mode": "append"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'foo.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 19,
+          "filename": "layout.multi.append.prepend.block.tokens.json"
+        }
+      ],
+      "line": 18,
+      "filename": "layout.multi.append.prepend.block.tokens.json",
+      "name": "head",
+      "mode": "prepend"
+    }
+  ],
+  "line": 0,
+  "filename": "layout.multi.append.prepend.block.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.multi.append.prepend.block.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.multi.append.prepend.block.tokens.json
new file mode 100644
index 0000000..10649e3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.multi.append.prepend.block.tokens.json
@@ -0,0 +1,41 @@
+{"type":"extends","line":1,"val":"../fixtures/multi-append-prepend-block/redefine"}
+{"type":"newline","line":3}
+{"type":"block","line":3,"val":"content","mode":"append"}
+{"type":"indent","line":4,"val":1}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"class","line":4,"val":"first"}
+{"type":"class","line":4,"val":"append"}
+{"type":"text","line":4,"val":"Something appended to content"}
+{"type":"outdent","line":6}
+{"type":"block","line":6,"val":"content","mode":"prepend"}
+{"type":"indent","line":7,"val":1}
+{"type":"tag","line":7,"val":"p","selfClosing":false}
+{"type":"class","line":7,"val":"first"}
+{"type":"class","line":7,"val":"prepend"}
+{"type":"text","line":7,"val":"Something prepended to content"}
+{"type":"outdent","line":9}
+{"type":"block","line":9,"val":"content","mode":"append"}
+{"type":"indent","line":10,"val":1}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"class","line":10,"val":"last"}
+{"type":"class","line":10,"val":"append"}
+{"type":"text","line":10,"val":"Last append must be most last"}
+{"type":"outdent","line":12}
+{"type":"block","line":12,"val":"content","mode":"prepend"}
+{"type":"indent","line":13,"val":1}
+{"type":"tag","line":13,"val":"p","selfClosing":false}
+{"type":"class","line":13,"val":"first"}
+{"type":"class","line":13,"val":"prepend"}
+{"type":"text","line":13,"val":"Last prepend must appear at top"}
+{"type":"outdent","line":15}
+{"type":"block","line":15,"val":"head","mode":"append"}
+{"type":"indent","line":16,"val":1}
+{"type":"tag","line":16,"val":"script","selfClosing":false}
+{"type":"attrs","line":16,"attrs":[{"name":"src","val":"'jquery.js'","escaped":true}]}
+{"type":"outdent","line":18}
+{"type":"block","line":18,"val":"head","mode":"prepend"}
+{"type":"indent","line":19,"val":1}
+{"type":"tag","line":19,"val":"script","selfClosing":false}
+{"type":"attrs","line":19,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"outdent","line":20}
+{"type":"eos","line":20}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.expected.json
new file mode 100644
index 0000000..97a7e23
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.expected.json
@@ -0,0 +1,67 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "../fixtures/prepend/app-layout",
+        "line": 2,
+        "filename": "layout.prepend.tokens.json"
+      },
+      "line": 2,
+      "filename": "layout.prepend.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'foo.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "layout.prepend.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'bar.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 6,
+          "filename": "layout.prepend.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "layout.prepend.tokens.json",
+      "name": "head",
+      "mode": "prepend"
+    }
+  ],
+  "line": 0,
+  "filename": "layout.prepend.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.tokens.json
new file mode 100644
index 0000000..721e04c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.tokens.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/prepend/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"prepend"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.without-block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.without-block.expected.json
new file mode 100644
index 0000000..a9156c5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.without-block.expected.json
@@ -0,0 +1,67 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Extends",
+      "file": {
+        "type": "FileReference",
+        "path": "../fixtures/prepend-without-block/app-layout",
+        "line": 2,
+        "filename": "layout.prepend.without-block.tokens.json"
+      },
+      "line": 2,
+      "filename": "layout.prepend.without-block.tokens.json"
+    },
+    {
+      "type": "NamedBlock",
+      "nodes": [
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'foo.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 5,
+          "filename": "layout.prepend.without-block.tokens.json"
+        },
+        {
+          "type": "Tag",
+          "name": "script",
+          "selfClosing": false,
+          "block": {
+            "type": "Block",
+            "nodes": []
+          },
+          "attrs": [
+            {
+              "name": "src",
+              "val": "'bar.js'",
+              "escaped": true
+            }
+          ],
+          "attributeBlocks": [],
+          "isInline": false,
+          "line": 6,
+          "filename": "layout.prepend.without-block.tokens.json"
+        }
+      ],
+      "line": 4,
+      "filename": "layout.prepend.without-block.tokens.json",
+      "name": "head",
+      "mode": "prepend"
+    }
+  ],
+  "line": 0,
+  "filename": "layout.prepend.without-block.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.without-block.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.without-block.tokens.json
new file mode 100644
index 0000000..f39e00b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/layout.prepend.without-block.tokens.json
@@ -0,0 +1,12 @@
+{"type":"newline","line":2}
+{"type":"extends","line":2,"val":"../fixtures/prepend-without-block/app-layout"}
+{"type":"newline","line":4}
+{"type":"block","line":4,"val":"head","mode":"prepend"}
+{"type":"indent","line":5,"val":2}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'foo.js'","escaped":true}]}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"src","val":"'bar.js'","escaped":true}]}
+{"type":"outdent","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-at-end-of-file.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-at-end-of-file.expected.json
new file mode 100644
index 0000000..15f8c7f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-at-end-of-file.expected.json
@@ -0,0 +1,61 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "./auxiliary/mixin-at-end-of-file.jade",
+        "line": 1,
+        "filename": "mixin-at-end-of-file.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "line": 1,
+      "filename": "mixin-at-end-of-file.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "slide",
+      "args": "",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "some awesome content",
+                  "line": 4,
+                  "filename": "mixin-at-end-of-file.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "mixin-at-end-of-file.tokens.json"
+          }
+        ],
+        "line": 3,
+        "filename": "mixin-at-end-of-file.tokens.json"
+      },
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 3,
+      "filename": "mixin-at-end-of-file.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin-at-end-of-file.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-at-end-of-file.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-at-end-of-file.tokens.json
new file mode 100644
index 0000000..9b5ff54
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-at-end-of-file.tokens.json
@@ -0,0 +1,8 @@
+{"type":"include","line":1,"val":"./auxiliary/mixin-at-end-of-file.jade"}
+{"type":"newline","line":3}
+{"type":"call","line":3,"val":"slide","args":""}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"some awesome content"}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-hoist.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-hoist.expected.json
new file mode 100644
index 0000000..e9be4a8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-hoist.expected.json
@@ -0,0 +1,85 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "h1",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "mixin-hoist.tokens.json",
+            "code": {
+              "type": "Code",
+              "val": "title",
+              "buffer": true,
+              "escape": true,
+              "line": 3,
+              "filename": "mixin-hoist.tokens.json"
+            }
+          }
+        ],
+        "line": 3,
+        "filename": "mixin-hoist.tokens.json"
+      },
+      "call": false,
+      "line": 2,
+      "filename": "mixin-hoist.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "foo",
+                  "args": null,
+                  "block": null,
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 7,
+                  "filename": "mixin-hoist.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 6,
+            "filename": "mixin-hoist.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "mixin-hoist.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin-hoist.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-hoist.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-hoist.tokens.json
new file mode 100644
index 0000000..fd0ea98
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-hoist.tokens.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"mixin","line":2,"val":"foo","args":null}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"code","line":3,"val":"title","escape":true,"buffer":true}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"html","selfClosing":false}
+{"type":"indent","line":6,"val":2}
+{"type":"tag","line":6,"val":"body","selfClosing":false}
+{"type":"indent","line":7,"val":4}
+{"type":"mixin","line":7,"val":"foo","args":null}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-via-include.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-via-include.expected.json
new file mode 100644
index 0000000..8bb8472
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-via-include.expected.json
@@ -0,0 +1,46 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Comment",
+      "val": " regression test for https://github.com/jadejs/jade/issues/1435",
+      "buffer": false,
+      "line": 1,
+      "filename": "mixin-via-include.tokens.json"
+    },
+    {
+      "type": "Text",
+      "val": "",
+      "line": 2,
+      "filename": "mixin-via-include.tokens.json"
+    },
+    {
+      "type": "Include",
+      "file": {
+        "type": "FileReference",
+        "path": "../fixtures/mixin-include.jade",
+        "line": 3,
+        "filename": "mixin-via-include.tokens.json"
+      },
+      "attrs": [],
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "line": 3,
+      "filename": "mixin-via-include.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "bang",
+      "block": null,
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 5,
+      "filename": "mixin-via-include.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin-via-include.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-via-include.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-via-include.tokens.json
new file mode 100644
index 0000000..439b3e4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin-via-include.tokens.json
@@ -0,0 +1,7 @@
+{"type":"comment","line":1,"val":" regression test for https://github.com/jadejs/jade/issues/1435","buffer":false}
+{"type":"text","line":2,"val":""}
+{"type":"newline","line":3}
+{"type":"include","line":3,"val":"../fixtures/mixin-include.jade"}
+{"type":"newline","line":5}
+{"type":"call","line":5,"val":"bang"}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.attrs.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.attrs.expected.json
new file mode 100644
index 0000000..e7225a7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.attrs.expected.json
@@ -0,0 +1,724 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "centered",
+      "args": "title",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Code",
+                  "val": "if (title)",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 3,
+                  "filename": "mixin.attrs.tokens.json",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "h1",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "class",
+                            "val": "attributes.class",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 4,
+                        "filename": "mixin.attrs.tokens.json",
+                        "code": {
+                          "type": "Code",
+                          "val": "title",
+                          "buffer": true,
+                          "escape": true,
+                          "line": 4,
+                          "filename": "mixin.attrs.tokens.json"
+                        }
+                      }
+                    ],
+                    "line": 4,
+                    "filename": "mixin.attrs.tokens.json"
+                  }
+                },
+                {
+                  "type": "MixinBlock",
+                  "line": 5,
+                  "filename": "mixin.attrs.tokens.json"
+                },
+                {
+                  "type": "Code",
+                  "val": "if (attributes.href)",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 6,
+                  "filename": "mixin.attrs.tokens.json",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "div",
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "a",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": [
+                                  {
+                                    "type": "Text",
+                                    "val": "Back",
+                                    "line": 8,
+                                    "filename": "mixin.attrs.tokens.json"
+                                  }
+                                ]
+                              },
+                              "attrs": [
+                                {
+                                  "name": "href",
+                                  "val": "attributes.href",
+                                  "escaped": true
+                                }
+                              ],
+                              "attributeBlocks": [],
+                              "isInline": true,
+                              "line": 8,
+                              "filename": "mixin.attrs.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [
+                          {
+                            "name": "class",
+                            "val": "'footer'",
+                            "escaped": false
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 7,
+                        "filename": "mixin.attrs.tokens.json"
+                      }
+                    ],
+                    "line": 7,
+                    "filename": "mixin.attrs.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'centered'",
+                "escaped": false
+              },
+              {
+                "name": "id",
+                "val": "attributes.id",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "mixin.attrs.tokens.json"
+          }
+        ],
+        "line": 2,
+        "filename": "mixin.attrs.tokens.json"
+      },
+      "call": false,
+      "line": 1,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "main",
+      "args": "title",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "centered",
+                  "args": "title",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "MixinBlock",
+                        "line": 13,
+                        "filename": "mixin.attrs.tokens.json"
+                      }
+                    ],
+                    "line": 12,
+                    "filename": "mixin.attrs.tokens.json"
+                  },
+                  "call": true,
+                  "attrs": [
+                    {
+                      "name": "class",
+                      "val": "'highlight'",
+                      "escaped": false
+                    }
+                  ],
+                  "attributeBlocks": [
+                    "attributes"
+                  ],
+                  "line": 12,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'stretch'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 11,
+            "filename": "mixin.attrs.tokens.json"
+          }
+        ],
+        "line": 11,
+        "filename": "mixin.attrs.tokens.json"
+      },
+      "call": false,
+      "line": 10,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "bottom",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "MixinBlock",
+                  "line": 17,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'bottom'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 16,
+            "filename": "mixin.attrs.tokens.json"
+          }
+        ],
+        "line": 16,
+        "filename": "mixin.attrs.tokens.json"
+      },
+      "call": false,
+      "line": 15,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "body",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Mixin",
+            "name": "centered",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Hello World",
+                  "line": 20,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ],
+              "line": 20,
+              "filename": "mixin.attrs.tokens.json"
+            },
+            "call": true,
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'First'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 20,
+            "filename": "mixin.attrs.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "centered",
+            "args": "'Section 1'",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "Some important content.",
+                        "line": 22,
+                        "filename": "mixin.attrs.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 22,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ],
+              "line": 21,
+              "filename": "mixin.attrs.tokens.json"
+            },
+            "call": true,
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'Second'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 21,
+            "filename": "mixin.attrs.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "centered",
+            "args": "'Section 2'",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "Even more important content.",
+                        "line": 24,
+                        "filename": "mixin.attrs.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 24,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ],
+              "line": 23,
+              "filename": "mixin.attrs.tokens.json"
+            },
+            "call": true,
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'Third'",
+                "escaped": false
+              },
+              {
+                "name": "class",
+                "val": "'foo'",
+                "escaped": false
+              },
+              {
+                "name": "href",
+                "val": "'menu.html'",
+                "escaped": true
+              },
+              {
+                "name": "class",
+                "val": "'bar'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 23,
+            "filename": "mixin.attrs.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "main",
+            "args": "'Section 3'",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "Last content.",
+                        "line": 26,
+                        "filename": "mixin.attrs.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 26,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ],
+              "line": 25,
+              "filename": "mixin.attrs.tokens.json"
+            },
+            "call": true,
+            "attrs": [
+              {
+                "name": "href",
+                "val": "'#'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 25,
+            "filename": "mixin.attrs.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "bottom",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "Some final words.",
+                        "line": 28,
+                        "filename": "mixin.attrs.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 28,
+                  "filename": "mixin.attrs.tokens.json"
+                }
+              ],
+              "line": 27,
+              "filename": "mixin.attrs.tokens.json"
+            },
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'foo'",
+                "escaped": false
+              },
+              {
+                "name": "class",
+                "val": "'bar'",
+                "escaped": true
+              },
+              {
+                "name": "name",
+                "val": "'end'",
+                "escaped": true
+              },
+              {
+                "name": "id",
+                "val": "'Last'",
+                "escaped": true
+              },
+              {
+                "name": "data-attr",
+                "val": "'baz'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 27,
+            "filename": "mixin.attrs.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "bottom",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "['class1', 'class2']",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 29,
+            "filename": "mixin.attrs.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 19,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'thing'",
+                "escaped": false
+              },
+              {
+                "name": "attr1",
+                "val": "'foo'",
+                "escaped": true
+              },
+              {
+                "name": "attr2",
+                "val": "'bar'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 32,
+            "filename": "mixin.attrs.tokens.json"
+          }
+        ],
+        "line": 32,
+        "filename": "mixin.attrs.tokens.json"
+      },
+      "call": false,
+      "line": 31,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var val = '<biz>'",
+      "buffer": false,
+      "escape": false,
+      "line": 34,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var classes = ['foo', 'bar']",
+      "buffer": false,
+      "escape": false,
+      "line": 35,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "block": null,
+      "call": true,
+      "attrs": [
+        {
+          "name": "attr3",
+          "val": "'baz'",
+          "escaped": true
+        },
+        {
+          "name": "data-foo",
+          "val": "val",
+          "escaped": true
+        },
+        {
+          "name": "data-bar",
+          "val": "val",
+          "escaped": false
+        },
+        {
+          "name": "class",
+          "val": "classes",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "'thunk'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "line": 36,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Comment",
+      "val": " Regression test for #1424",
+      "buffer": false,
+      "line": 38,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "work_filmstrip_item",
+      "args": "work",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 40,
+            "filename": "mixin.attrs.tokens.json",
+            "code": {
+              "type": "Code",
+              "val": "work",
+              "buffer": true,
+              "escape": true,
+              "line": 40,
+              "filename": "mixin.attrs.tokens.json"
+            }
+          }
+        ],
+        "line": 40,
+        "filename": "mixin.attrs.tokens.json"
+      },
+      "call": false,
+      "line": 39,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "work_filmstrip_item",
+      "args": "'work'",
+      "block": null,
+      "call": true,
+      "attrs": [
+        {
+          "name": "data-profile",
+          "val": "'profile'",
+          "escaped": true
+        },
+        {
+          "name": "data-creator-name",
+          "val": "'name'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "line": 41,
+      "filename": "mixin.attrs.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "block": null,
+      "call": true,
+      "attrs": [
+        {
+          "name": "attr3",
+          "val": "\"qux\"",
+          "escaped": true
+        },
+        {
+          "name": "class",
+          "val": "\"baz\"",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "line": 43,
+      "filename": "mixin.attrs.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin.attrs.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.attrs.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.attrs.tokens.json
new file mode 100644
index 0000000..285c48a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.attrs.tokens.json
@@ -0,0 +1,113 @@
+{"type":"mixin","line":1,"val":"centered","args":"title"}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"div","selfClosing":false}
+{"type":"class","line":2,"val":"centered"}
+{"type":"attrs","line":2,"attrs":[{"name":"id","val":"attributes.id","escaped":true}]}
+{"type":"indent","line":3,"val":4}
+{"type":"code","line":3,"val":"if (title)","escape":false,"buffer":false}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"class","val":"attributes.class","escaped":true}]}
+{"type":"code","line":4,"val":"title","escape":true,"buffer":true}
+{"type":"outdent","line":5}
+{"type":"mixin-block","line":5}
+{"type":"newline","line":6}
+{"type":"code","line":6,"val":"if (attributes.href)","escape":false,"buffer":false}
+{"type":"indent","line":7,"val":6}
+{"type":"class","line":7,"val":"footer"}
+{"type":"indent","line":8,"val":8}
+{"type":"tag","line":8,"val":"a","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"href","val":"attributes.href","escaped":true}]}
+{"type":"text","line":8,"val":"Back"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"mixin","line":10,"val":"main","args":"title"}
+{"type":"indent","line":11,"val":2}
+{"type":"tag","line":11,"val":"div","selfClosing":false}
+{"type":"class","line":11,"val":"stretch"}
+{"type":"indent","line":12,"val":4}
+{"type":"call","line":12,"val":"centered","args":"title"}
+{"type":"class","line":12,"val":"highlight"}
+{"type":"&attributes","line":12,"val":"attributes"}
+{"type":"indent","line":13,"val":6}
+{"type":"mixin-block","line":13}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"mixin","line":15,"val":"bottom","args":null}
+{"type":"indent","line":16,"val":2}
+{"type":"tag","line":16,"val":"div","selfClosing":false}
+{"type":"class","line":16,"val":"bottom"}
+{"type":"&attributes","line":16,"val":"attributes"}
+{"type":"indent","line":17,"val":4}
+{"type":"mixin-block","line":17}
+{"type":"outdent","line":19}
+{"type":"outdent","line":19}
+{"type":"tag","line":19,"val":"body","selfClosing":false}
+{"type":"indent","line":20,"val":2}
+{"type":"call","line":20,"val":"centered"}
+{"type":"id","line":20,"val":"First"}
+{"type":"text","line":20,"val":"Hello World"}
+{"type":"newline","line":21}
+{"type":"call","line":21,"val":"centered","args":"'Section 1'"}
+{"type":"id","line":21,"val":"Second"}
+{"type":"indent","line":22,"val":4}
+{"type":"tag","line":22,"val":"p","selfClosing":false}
+{"type":"text","line":22,"val":"Some important content."}
+{"type":"outdent","line":23}
+{"type":"call","line":23,"val":"centered","args":"'Section 2'"}
+{"type":"id","line":23,"val":"Third"}
+{"type":"class","line":23,"val":"foo"}
+{"type":"attrs","line":23,"attrs":[{"name":"href","val":"'menu.html'","escaped":true},{"name":"class","val":"'bar'","escaped":true}]}
+{"type":"indent","line":24,"val":4}
+{"type":"tag","line":24,"val":"p","selfClosing":false}
+{"type":"text","line":24,"val":"Even more important content."}
+{"type":"outdent","line":25}
+{"type":"call","line":25,"val":"main","args":"'Section 3'"}
+{"type":"attrs","line":25,"attrs":[{"name":"href","val":"'#'","escaped":true}]}
+{"type":"indent","line":26,"val":4}
+{"type":"tag","line":26,"val":"p","selfClosing":false}
+{"type":"text","line":26,"val":"Last content."}
+{"type":"outdent","line":27}
+{"type":"call","line":27,"val":"bottom"}
+{"type":"class","line":27,"val":"foo"}
+{"type":"attrs","line":27,"attrs":[{"name":"class","val":"'bar'","escaped":true},{"name":"name","val":"'end'","escaped":true},{"name":"id","val":"'Last'","escaped":true},{"name":"data-attr","val":"'baz'","escaped":true}]}
+{"type":"indent","line":28,"val":4}
+{"type":"tag","line":28,"val":"p","selfClosing":false}
+{"type":"text","line":28,"val":"Some final words."}
+{"type":"outdent","line":29}
+{"type":"call","line":29,"val":"bottom"}
+{"type":"attrs","line":29,"attrs":[{"name":"class","val":"['class1', 'class2']","escaped":true}]}
+{"type":"outdent","line":31}
+{"type":"mixin","line":31,"val":"foo","args":null}
+{"type":"indent","line":32,"val":2}
+{"type":"tag","line":32,"val":"div","selfClosing":false}
+{"type":"class","line":32,"val":"thing"}
+{"type":"attrs","line":32,"attrs":[{"name":"attr1","val":"'foo'","escaped":true},{"name":"attr2","val":"'bar'","escaped":true}]}
+{"type":"&attributes","line":32,"val":"attributes"}
+{"type":"outdent","line":34}
+{"type":"code","line":34,"val":"var val = '<biz>'","escape":false,"buffer":false}
+{"type":"newline","line":35}
+{"type":"code","line":35,"val":"var classes = ['foo', 'bar']","escape":false,"buffer":false}
+{"type":"newline","line":36}
+{"type":"call","line":36,"val":"foo"}
+{"type":"attrs","line":36,"attrs":[{"name":"attr3","val":"'baz'","escaped":true},{"name":"data-foo","val":"val","escaped":true},{"name":"data-bar","val":"val","escaped":false},{"name":"class","val":"classes","escaped":true}]}
+{"type":"class","line":36,"val":"thunk"}
+{"type":"newline","line":38}
+{"type":"comment","line":38,"val":" Regression test for #1424","buffer":false}
+{"type":"newline","line":39}
+{"type":"mixin","line":39,"val":"work_filmstrip_item","args":"work"}
+{"type":"indent","line":40,"val":2}
+{"type":"tag","line":40,"val":"div","selfClosing":false}
+{"type":"&attributes","line":40,"val":"attributes"}
+{"type":"code","line":40,"val":"work","escape":true,"buffer":true}
+{"type":"outdent","line":41}
+{"type":"call","line":41,"val":"work_filmstrip_item","args":"'work'"}
+{"type":"attrs","line":41,"attrs":[{"name":"data-profile","val":"'profile'","escaped":true},{"name":"data-creator-name","val":"'name'","escaped":true}]}
+{"type":"newline","line":43}
+{"type":"call","line":43,"val":"foo"}
+{"type":"attrs","line":43,"attrs":[{"name":"attr3","val":"\"qux\"","escaped":true},{"name":"class","val":"\"baz\"","escaped":true}]}
+{"type":"newline","line":47}
+{"type":"eos","line":47}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.block-tag-behaviour.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.block-tag-behaviour.expected.json
new file mode 100644
index 0000000..7575d58
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.block-tag-behaviour.expected.json
@@ -0,0 +1,305 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "article",
+      "args": "name",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "section",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "mixin.block-tag-behaviour.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "name",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 4,
+                    "filename": "mixin.block-tag-behaviour.tokens.json"
+                  }
+                },
+                {
+                  "type": "MixinBlock",
+                  "line": 5,
+                  "filename": "mixin.block-tag-behaviour.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'article'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "mixin.block-tag-behaviour.tokens.json"
+          }
+        ],
+        "line": 3,
+        "filename": "mixin.block-tag-behaviour.tokens.json"
+      },
+      "call": false,
+      "line": 2,
+      "filename": "mixin.block-tag-behaviour.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "article",
+                  "args": "'Foo'",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "p",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "I'm article foo",
+                              "line": 9,
+                              "filename": "mixin.block-tag-behaviour.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 9,
+                        "filename": "mixin.block-tag-behaviour.tokens.json"
+                      }
+                    ]
+                  },
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 9,
+                  "filename": "mixin.block-tag-behaviour.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "mixin.block-tag-behaviour.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 7,
+      "filename": "mixin.block-tag-behaviour.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "article",
+      "args": "name",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "section",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 13,
+                  "filename": "mixin.block-tag-behaviour.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "name",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 13,
+                    "filename": "mixin.block-tag-behaviour.tokens.json"
+                  }
+                },
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "MixinBlock",
+                        "line": 15,
+                        "filename": "mixin.block-tag-behaviour.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 14,
+                  "filename": "mixin.block-tag-behaviour.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'article'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 12,
+            "filename": "mixin.block-tag-behaviour.tokens.json"
+          }
+        ],
+        "line": 12,
+        "filename": "mixin.block-tag-behaviour.tokens.json"
+      },
+      "call": false,
+      "line": 11,
+      "filename": "mixin.block-tag-behaviour.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "article",
+                  "args": "'Something'",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "I'm a much longer",
+                        "line": 20
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 21
+                      },
+                      {
+                        "type": "Text",
+                        "val": "text-only article,",
+                        "line": 21
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 22
+                      },
+                      {
+                        "type": "Text",
+                        "val": "but you can still",
+                        "line": 22
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 23
+                      },
+                      {
+                        "type": "Text",
+                        "val": "inline html tags",
+                        "line": 23
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 24
+                      },
+                      {
+                        "type": "Text",
+                        "val": "in me if you want.",
+                        "line": 24
+                      }
+                    ]
+                  },
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 19,
+                  "filename": "mixin.block-tag-behaviour.tokens.json",
+                  "textOnly": true
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 18,
+            "filename": "mixin.block-tag-behaviour.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 17,
+      "filename": "mixin.block-tag-behaviour.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin.block-tag-behaviour.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.block-tag-behaviour.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.block-tag-behaviour.tokens.json
new file mode 100644
index 0000000..be7a65b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.block-tag-behaviour.tokens.json
@@ -0,0 +1,56 @@
+{"type":"newline","line":2}
+{"type":"mixin","line":2,"val":"article","args":"name"}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"section","selfClosing":false}
+{"type":"class","line":3,"val":"article"}
+{"type":"indent","line":4,"val":4}
+{"type":"tag","line":4,"val":"h1","selfClosing":false}
+{"type":"code","line":4,"val":"name","escape":true,"buffer":true}
+{"type":"newline","line":5}
+{"type":"mixin-block","line":5}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"html","selfClosing":false}
+{"type":"indent","line":8,"val":2}
+{"type":"tag","line":8,"val":"body","selfClosing":false}
+{"type":"indent","line":9,"val":4}
+{"type":"call","line":9,"val":"article","args":"'Foo'"}
+{"type":":","line":9}
+{"type":"tag","line":9,"val":"p","selfClosing":false}
+{"type":"text","line":9,"val":"I'm article foo"}
+{"type":"outdent","line":11}
+{"type":"outdent","line":11}
+{"type":"mixin","line":11,"val":"article","args":"name"}
+{"type":"indent","line":12,"val":2}
+{"type":"tag","line":12,"val":"section","selfClosing":false}
+{"type":"class","line":12,"val":"article"}
+{"type":"indent","line":13,"val":4}
+{"type":"tag","line":13,"val":"h1","selfClosing":false}
+{"type":"code","line":13,"val":"name","escape":true,"buffer":true}
+{"type":"newline","line":14}
+{"type":"tag","line":14,"val":"p","selfClosing":false}
+{"type":"indent","line":15,"val":6}
+{"type":"mixin-block","line":15}
+{"type":"outdent","line":17}
+{"type":"outdent","line":17}
+{"type":"outdent","line":17}
+{"type":"tag","line":17,"val":"html","selfClosing":false}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"body","selfClosing":false}
+{"type":"indent","line":19,"val":4}
+{"type":"call","line":19,"val":"article","args":"'Something'"}
+{"type":"dot","line":19}
+{"type":"start-pipeless-text","line":19}
+{"type":"text","line":20,"val":"I'm a much longer"}
+{"type":"newline","line":21}
+{"type":"text","line":21,"val":"text-only article,"}
+{"type":"newline","line":22}
+{"type":"text","line":22,"val":"but you can still"}
+{"type":"newline","line":23}
+{"type":"text","line":23,"val":"inline html tags"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"in me if you want."}
+{"type":"end-pipeless-text","line":24}
+{"type":"outdent","line":24}
+{"type":"outdent","line":24}
+{"type":"eos","line":24}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.blocks.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.blocks.expected.json
new file mode 100644
index 0000000..b723da0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.blocks.expected.json
@@ -0,0 +1,576 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "form",
+      "args": "method, action",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "form",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Code",
+                  "val": "var csrf_token_from_somewhere = 'hey'",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 5,
+                  "filename": "mixin.blocks.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "input",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "type",
+                      "val": "'hidden'",
+                      "escaped": true
+                    },
+                    {
+                      "name": "name",
+                      "val": "'_csrf'",
+                      "escaped": true
+                    },
+                    {
+                      "name": "value",
+                      "val": "csrf_token_from_somewhere",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 6,
+                  "filename": "mixin.blocks.tokens.json"
+                },
+                {
+                  "type": "MixinBlock",
+                  "line": 7,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "method",
+                "val": "method",
+                "escaped": true
+              },
+              {
+                "name": "action",
+                "val": "action",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ],
+        "line": 4,
+        "filename": "mixin.blocks.tokens.json"
+      },
+      "call": false,
+      "line": 3,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "form",
+                  "args": "'GET', '/search'",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "input",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "val": "'text'",
+                            "escaped": true
+                          },
+                          {
+                            "name": "name",
+                            "val": "'query'",
+                            "escaped": true
+                          },
+                          {
+                            "name": "placeholder",
+                            "val": "'Search'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 12,
+                        "filename": "mixin.blocks.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "input",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "val": "'submit'",
+                            "escaped": true
+                          },
+                          {
+                            "name": "value",
+                            "val": "'Search'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 13,
+                        "filename": "mixin.blocks.tokens.json"
+                      }
+                    ],
+                    "line": 11,
+                    "filename": "mixin.blocks.tokens.json"
+                  },
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 11,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 10,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 9,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "form",
+                  "args": "'POST', '/search'",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "input",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "val": "'text'",
+                            "escaped": true
+                          },
+                          {
+                            "name": "name",
+                            "val": "'query'",
+                            "escaped": true
+                          },
+                          {
+                            "name": "placeholder",
+                            "val": "'Search'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 18,
+                        "filename": "mixin.blocks.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "input",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "type",
+                            "val": "'submit'",
+                            "escaped": true
+                          },
+                          {
+                            "name": "value",
+                            "val": "'Search'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 19,
+                        "filename": "mixin.blocks.tokens.json"
+                      }
+                    ],
+                    "line": 17,
+                    "filename": "mixin.blocks.tokens.json"
+                  },
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 17,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 16,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 15,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "form",
+                  "args": "'POST', '/search'",
+                  "block": null,
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 23,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 22,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 21,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "bar",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "MixinBlock",
+                  "line": 27,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'bar'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 26,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ],
+        "line": 26,
+        "filename": "mixin.blocks.tokens.json"
+      },
+      "call": false,
+      "line": 25,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "bar",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "MixinBlock",
+                        "line": 32,
+                        "filename": "mixin.blocks.tokens.json"
+                      }
+                    ],
+                    "line": 31,
+                    "filename": "mixin.blocks.tokens.json"
+                  },
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 31,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'foo'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 30,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ],
+        "line": 30,
+        "filename": "mixin.blocks.tokens.json"
+      },
+      "call": false,
+      "line": 29,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "one",
+                  "line": 35,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 35,
+            "filename": "mixin.blocks.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "two",
+                  "line": 36,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 36,
+            "filename": "mixin.blocks.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "three",
+                  "line": 37,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 37,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ],
+        "line": 34,
+        "filename": "mixin.blocks.tokens.json"
+      },
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 34,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "baz",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "MixinBlock",
+                  "line": 42,
+                  "filename": "mixin.blocks.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'baz'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 41,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ],
+        "line": 41,
+        "filename": "mixin.blocks.tokens.json"
+      },
+      "call": false,
+      "line": 40,
+      "filename": "mixin.blocks.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "baz",
+      "args": "",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Code",
+            "val": "'123'",
+            "buffer": true,
+            "escape": true,
+            "line": 44,
+            "filename": "mixin.blocks.tokens.json"
+          }
+        ],
+        "line": 44,
+        "filename": "mixin.blocks.tokens.json"
+      },
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 44,
+      "filename": "mixin.blocks.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin.blocks.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.blocks.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.blocks.tokens.json
new file mode 100644
index 0000000..e336f46
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.blocks.tokens.json
@@ -0,0 +1,87 @@
+{"type":"newline","line":3}
+{"type":"mixin","line":3,"val":"form","args":"method, action"}
+{"type":"indent","line":4,"val":2}
+{"type":"tag","line":4,"val":"form","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"method","val":"method","escaped":true},{"name":"action","val":"action","escaped":true}]}
+{"type":"indent","line":5,"val":4}
+{"type":"code","line":5,"val":"var csrf_token_from_somewhere = 'hey'","escape":false,"buffer":false}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"input","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'hidden'","escaped":true},{"name":"name","val":"'_csrf'","escaped":true},{"name":"value","val":"csrf_token_from_somewhere","escaped":true}]}
+{"type":"newline","line":7}
+{"type":"mixin-block","line":7}
+{"type":"outdent","line":9}
+{"type":"outdent","line":9}
+{"type":"tag","line":9,"val":"html","selfClosing":false}
+{"type":"indent","line":10,"val":2}
+{"type":"tag","line":10,"val":"body","selfClosing":false}
+{"type":"indent","line":11,"val":4}
+{"type":"call","line":11,"val":"form","args":"'GET', '/search'"}
+{"type":"indent","line":12,"val":6}
+{"type":"tag","line":12,"val":"input","selfClosing":false}
+{"type":"attrs","line":12,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'query'","escaped":true},{"name":"placeholder","val":"'Search'","escaped":true}]}
+{"type":"newline","line":13}
+{"type":"tag","line":13,"val":"input","selfClosing":false}
+{"type":"attrs","line":13,"attrs":[{"name":"type","val":"'submit'","escaped":true},{"name":"value","val":"'Search'","escaped":true}]}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"tag","line":15,"val":"html","selfClosing":false}
+{"type":"indent","line":16,"val":2}
+{"type":"tag","line":16,"val":"body","selfClosing":false}
+{"type":"indent","line":17,"val":4}
+{"type":"call","line":17,"val":"form","args":"'POST', '/search'"}
+{"type":"indent","line":18,"val":6}
+{"type":"tag","line":18,"val":"input","selfClosing":false}
+{"type":"attrs","line":18,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'query'","escaped":true},{"name":"placeholder","val":"'Search'","escaped":true}]}
+{"type":"newline","line":19}
+{"type":"tag","line":19,"val":"input","selfClosing":false}
+{"type":"attrs","line":19,"attrs":[{"name":"type","val":"'submit'","escaped":true},{"name":"value","val":"'Search'","escaped":true}]}
+{"type":"outdent","line":21}
+{"type":"outdent","line":21}
+{"type":"outdent","line":21}
+{"type":"tag","line":21,"val":"html","selfClosing":false}
+{"type":"indent","line":22,"val":2}
+{"type":"tag","line":22,"val":"body","selfClosing":false}
+{"type":"indent","line":23,"val":4}
+{"type":"call","line":23,"val":"form","args":"'POST', '/search'"}
+{"type":"outdent","line":25}
+{"type":"outdent","line":25}
+{"type":"mixin","line":25,"val":"bar","args":null}
+{"type":"indent","line":26,"val":2}
+{"type":"id","line":26,"val":"bar"}
+{"type":"indent","line":27,"val":4}
+{"type":"mixin-block","line":27}
+{"type":"outdent","line":29}
+{"type":"outdent","line":29}
+{"type":"mixin","line":29,"val":"foo","args":null}
+{"type":"indent","line":30,"val":2}
+{"type":"id","line":30,"val":"foo"}
+{"type":"indent","line":31,"val":4}
+{"type":"call","line":31,"val":"bar"}
+{"type":"indent","line":32,"val":6}
+{"type":"mixin-block","line":32}
+{"type":"outdent","line":34}
+{"type":"outdent","line":34}
+{"type":"outdent","line":34}
+{"type":"call","line":34,"val":"foo"}
+{"type":"indent","line":35,"val":2}
+{"type":"tag","line":35,"val":"p","selfClosing":false}
+{"type":"text","line":35,"val":"one"}
+{"type":"newline","line":36}
+{"type":"tag","line":36,"val":"p","selfClosing":false}
+{"type":"text","line":36,"val":"two"}
+{"type":"newline","line":37}
+{"type":"tag","line":37,"val":"p","selfClosing":false}
+{"type":"text","line":37,"val":"three"}
+{"type":"outdent","line":40}
+{"type":"mixin","line":40,"val":"baz","args":null}
+{"type":"indent","line":41,"val":4}
+{"type":"id","line":41,"val":"baz"}
+{"type":"indent","line":42,"val":8}
+{"type":"mixin-block","line":42}
+{"type":"outdent","line":44}
+{"type":"outdent","line":44}
+{"type":"call","line":44,"val":"baz","args":""}
+{"type":"code","line":44,"val":"'123'","escape":true,"buffer":true}
+{"type":"eos","line":44}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.merge.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.merge.expected.json
new file mode 100644
index 0000000..78ffe1f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.merge.expected.json
@@ -0,0 +1,290 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "foo",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "One",
+                  "line": 2,
+                  "filename": "mixin.merge.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'bar'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 2,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Two",
+                  "line": 3,
+                  "filename": "mixin.merge.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'baz'",
+                "escaped": false
+              },
+              {
+                "name": "class",
+                "val": "'quux'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 3,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Three",
+                  "line": 4,
+                  "filename": "mixin.merge.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 4,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "p",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Four",
+                  "line": 5,
+                  "filename": "mixin.merge.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'bar'",
+                "escaped": false
+              },
+              {
+                "name": "class",
+                "val": "\"baz\"",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [
+              "attributes"
+            ],
+            "isInline": false,
+            "line": 5,
+            "filename": "mixin.merge.tokens.json"
+          }
+        ],
+        "line": 2,
+        "filename": "mixin.merge.tokens.json"
+      },
+      "call": false,
+      "line": 1,
+      "filename": "mixin.merge.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "body",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'hello'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 8,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'world'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 9,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'hello'",
+                "escaped": false
+              },
+              {
+                "name": "id",
+                "val": "'world'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 10,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'hello'",
+                "escaped": false
+              },
+              {
+                "name": "class",
+                "val": "'world'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 11,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "\"hello\"",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 12,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'hello'",
+                "escaped": false
+              },
+              {
+                "name": "class",
+                "val": "\"world\"",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 13,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [],
+            "attributeBlocks": [],
+            "line": 14,
+            "filename": "mixin.merge.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "foo",
+            "block": null,
+            "call": true,
+            "attrs": [],
+            "attributeBlocks": [
+              "{class: \"hello\"}"
+            ],
+            "line": 15,
+            "filename": "mixin.merge.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 7,
+      "filename": "mixin.merge.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixin.merge.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.merge.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.merge.tokens.json
new file mode 100644
index 0000000..0f1353d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixin.merge.tokens.json
@@ -0,0 +1,52 @@
+{"type":"mixin","line":1,"val":"foo","args":null}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"class","line":2,"val":"bar"}
+{"type":"&attributes","line":2,"val":"attributes"}
+{"type":"text","line":2,"val":"One"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"class","line":3,"val":"baz"}
+{"type":"class","line":3,"val":"quux"}
+{"type":"&attributes","line":3,"val":"attributes"}
+{"type":"text","line":3,"val":"Two"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"&attributes","line":4,"val":"attributes"}
+{"type":"text","line":4,"val":"Three"}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"p","selfClosing":false}
+{"type":"class","line":5,"val":"bar"}
+{"type":"&attributes","line":5,"val":"attributes"}
+{"type":"attrs","line":5,"attrs":[{"name":"class","val":"\"baz\"","escaped":true}]}
+{"type":"text","line":5,"val":"Four"}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"body","selfClosing":false}
+{"type":"indent","line":8,"val":2}
+{"type":"call","line":8,"val":"foo"}
+{"type":"class","line":8,"val":"hello"}
+{"type":"newline","line":9}
+{"type":"call","line":9,"val":"foo"}
+{"type":"id","line":9,"val":"world"}
+{"type":"newline","line":10}
+{"type":"call","line":10,"val":"foo"}
+{"type":"class","line":10,"val":"hello"}
+{"type":"id","line":10,"val":"world"}
+{"type":"newline","line":11}
+{"type":"call","line":11,"val":"foo"}
+{"type":"class","line":11,"val":"hello"}
+{"type":"class","line":11,"val":"world"}
+{"type":"newline","line":12}
+{"type":"call","line":12,"val":"foo"}
+{"type":"attrs","line":12,"attrs":[{"name":"class","val":"\"hello\"","escaped":true}]}
+{"type":"newline","line":13}
+{"type":"call","line":13,"val":"foo"}
+{"type":"class","line":13,"val":"hello"}
+{"type":"attrs","line":13,"attrs":[{"name":"class","val":"\"world\"","escaped":true}]}
+{"type":"newline","line":14}
+{"type":"call","line":14,"val":"foo"}
+{"type":"newline","line":15}
+{"type":"call","line":15,"val":"foo"}
+{"type":"&attributes","line":15,"val":"{class: \"hello\"}"}
+{"type":"outdent","line":15}
+{"type":"eos","line":15}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins-unused.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins-unused.expected.json
new file mode 100644
index 0000000..560f784
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins-unused.expected.json
@@ -0,0 +1,62 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "never-called",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "This isn't something we ever want to output",
+                  "line": 2,
+                  "filename": "mixins-unused.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'wtf'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "mixins-unused.tokens.json"
+          }
+        ],
+        "line": 2,
+        "filename": "mixins-unused.tokens.json"
+      },
+      "call": false,
+      "line": 1,
+      "filename": "mixins-unused.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "body",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "mixins-unused.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixins-unused.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins-unused.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins-unused.tokens.json
new file mode 100644
index 0000000..782a577
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins-unused.tokens.json
@@ -0,0 +1,7 @@
+{"type":"mixin","line":1,"val":"never-called","args":null}
+{"type":"indent","line":2,"val":2}
+{"type":"class","line":2,"val":"wtf"}
+{"type":"text","line":2,"val":"This isn't something we ever want to output"}
+{"type":"outdent","line":3}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.expected.json
new file mode 100644
index 0000000..0305e3f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.expected.json
@@ -0,0 +1,452 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "comment",
+      "args": "title, str",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h2",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "mixins.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "title",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 3,
+                    "filename": "mixins.tokens.json"
+                  }
+                },
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "class",
+                      "val": "'body'",
+                      "escaped": false
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "mixins.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "str",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 4,
+                    "filename": "mixins.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'comment'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "mixins.tokens.json"
+          }
+        ],
+        "line": 2,
+        "filename": "mixins.tokens.json"
+      },
+      "call": false,
+      "line": 1,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "comment",
+      "args": "title, str",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h2",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 9,
+                  "filename": "mixins.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "title",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 9,
+                    "filename": "mixins.tokens.json"
+                  }
+                },
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "class",
+                      "val": "'body'",
+                      "escaped": false
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 10,
+                  "filename": "mixins.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "str",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 10,
+                    "filename": "mixins.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'comment'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "mixins.tokens.json"
+          }
+        ],
+        "line": 8,
+        "filename": "mixins.tokens.json"
+      },
+      "call": false,
+      "line": 7,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "h1",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Tobi",
+                  "line": 13,
+                  "filename": "mixins.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 13,
+            "filename": "mixins.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Mixin",
+                  "name": "comment",
+                  "args": "'This',\n            (('is regular, javascript'))",
+                  "block": null,
+                  "call": true,
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "line": 15,
+                  "filename": "mixins.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "class",
+                "val": "'comments'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 14,
+            "filename": "mixins.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "id",
+          "val": "'user'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 12,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "list",
+      "args": null,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "ul",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "foo",
+                        "line": 19,
+                        "filename": "mixins.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 19,
+                  "filename": "mixins.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "bar",
+                        "line": 20,
+                        "filename": "mixins.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 20,
+                  "filename": "mixins.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "baz",
+                        "line": 21,
+                        "filename": "mixins.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 21,
+                  "filename": "mixins.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 18,
+            "filename": "mixins.tokens.json"
+          }
+        ],
+        "line": 18,
+        "filename": "mixins.tokens.json"
+      },
+      "call": false,
+      "line": 17,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "body",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Mixin",
+            "name": "list",
+            "args": null,
+            "block": null,
+            "call": true,
+            "attrs": [],
+            "attributeBlocks": [],
+            "line": 24,
+            "filename": "mixins.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "list",
+            "args": "",
+            "block": null,
+            "call": true,
+            "attrs": [],
+            "attributeBlocks": [],
+            "line": 25,
+            "filename": "mixins.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "list",
+            "args": "",
+            "block": null,
+            "call": true,
+            "attrs": [],
+            "attributeBlocks": [],
+            "line": 26,
+            "filename": "mixins.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 23,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "foobar",
+      "args": "str",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'interpolation'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 29,
+            "filename": "mixins.tokens.json",
+            "code": {
+              "type": "Code",
+              "val": "str + 'interpolated'",
+              "buffer": true,
+              "escape": true,
+              "line": 29,
+              "filename": "mixins.tokens.json"
+            }
+          }
+        ],
+        "line": 29,
+        "filename": "mixins.tokens.json"
+      },
+      "call": false,
+      "line": 28,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var suffix = \"bar\"",
+      "buffer": false,
+      "escape": false,
+      "line": 31,
+      "filename": "mixins.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "#{'foo' + suffix}",
+      "args": "'This is '",
+      "block": null,
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 32,
+      "filename": "mixins.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixins.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.rest-args.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.rest-args.expected.json
new file mode 100644
index 0000000..bf860d4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.rest-args.expected.json
@@ -0,0 +1,85 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Mixin",
+      "name": "list",
+      "args": "tag, ...items",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "tag",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Each",
+                  "obj": "items",
+                  "val": "item",
+                  "key": "$index",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "li",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 4,
+                        "filename": "mixins.rest-args.tokens.json",
+                        "code": {
+                          "type": "Code",
+                          "val": "item",
+                          "buffer": true,
+                          "escape": true,
+                          "line": 4,
+                          "filename": "mixins.rest-args.tokens.json"
+                        }
+                      }
+                    ],
+                    "line": 4,
+                    "filename": "mixins.rest-args.tokens.json"
+                  },
+                  "line": 3,
+                  "filename": "mixins.rest-args.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "buffer": true,
+            "isInline": false,
+            "line": 2,
+            "filename": "mixins.rest-args.tokens.json"
+          }
+        ],
+        "line": 2,
+        "filename": "mixins.rest-args.tokens.json"
+      },
+      "call": false,
+      "line": 1,
+      "filename": "mixins.rest-args.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "list",
+      "args": "'ul', 1, 2, 3, 4",
+      "block": null,
+      "call": true,
+      "attrs": [],
+      "attributeBlocks": [],
+      "line": 6,
+      "filename": "mixins.rest-args.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "mixins.rest-args.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.rest-args.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.rest-args.tokens.json
new file mode 100644
index 0000000..69194c3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.rest-args.tokens.json
@@ -0,0 +1,14 @@
+{"type":"mixin","line":1,"val":"list","args":"tag, ...items"}
+{"type":"indent","line":2,"val":2}
+{"type":"interpolation","line":2,"val":"tag"}
+{"type":"indent","line":3,"val":4}
+{"type":"each","line":3,"val":"item","key":"$index","code":"items"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"li","selfClosing":false}
+{"type":"code","line":4,"val":"item","escape":true,"buffer":true}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"call","line":6,"val":"list","args":"'ul', 1, 2, 3, 4"}
+{"type":"newline","line":7}
+{"type":"eos","line":7}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.tokens.json
new file mode 100644
index 0000000..ecfb4d6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/mixins.tokens.json
@@ -0,0 +1,67 @@
+{"type":"mixin","line":1,"val":"comment","args":"title, str"}
+{"type":"indent","line":2,"val":2}
+{"type":"class","line":2,"val":"comment"}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h2","selfClosing":false}
+{"type":"code","line":3,"val":"title","escape":true,"buffer":true}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"class","line":4,"val":"body"}
+{"type":"code","line":4,"val":"str","escape":true,"buffer":true}
+{"type":"outdent","line":7}
+{"type":"outdent","line":7}
+{"type":"mixin","line":7,"val":"comment","args":"title, str"}
+{"type":"indent","line":8,"val":2}
+{"type":"class","line":8,"val":"comment"}
+{"type":"indent","line":9,"val":4}
+{"type":"tag","line":9,"val":"h2","selfClosing":false}
+{"type":"code","line":9,"val":"title","escape":true,"buffer":true}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"class","line":10,"val":"body"}
+{"type":"code","line":10,"val":"str","escape":true,"buffer":true}
+{"type":"outdent","line":12}
+{"type":"outdent","line":12}
+{"type":"id","line":12,"val":"user"}
+{"type":"indent","line":13,"val":2}
+{"type":"tag","line":13,"val":"h1","selfClosing":false}
+{"type":"text","line":13,"val":"Tobi"}
+{"type":"newline","line":14}
+{"type":"class","line":14,"val":"comments"}
+{"type":"indent","line":15,"val":4}
+{"type":"call","line":15,"val":"comment","args":"'This',\n            (('is regular, javascript'))"}
+{"type":"outdent","line":17}
+{"type":"outdent","line":17}
+{"type":"mixin","line":17,"val":"list","args":null}
+{"type":"indent","line":18,"val":2}
+{"type":"tag","line":18,"val":"ul","selfClosing":false}
+{"type":"indent","line":19,"val":4}
+{"type":"tag","line":19,"val":"li","selfClosing":false}
+{"type":"text","line":19,"val":"foo"}
+{"type":"newline","line":20}
+{"type":"tag","line":20,"val":"li","selfClosing":false}
+{"type":"text","line":20,"val":"bar"}
+{"type":"newline","line":21}
+{"type":"tag","line":21,"val":"li","selfClosing":false}
+{"type":"text","line":21,"val":"baz"}
+{"type":"outdent","line":23}
+{"type":"outdent","line":23}
+{"type":"tag","line":23,"val":"body","selfClosing":false}
+{"type":"indent","line":24,"val":2}
+{"type":"mixin","line":24,"val":"list","args":null}
+{"type":"newline","line":25}
+{"type":"call","line":25,"val":"list","args":""}
+{"type":"newline","line":26}
+{"type":"call","line":26,"val":"list","args":""}
+{"type":"outdent","line":28}
+{"type":"mixin","line":28,"val":"foobar","args":"str"}
+{"type":"indent","line":29,"val":2}
+{"type":"tag","line":29,"val":"div","selfClosing":false}
+{"type":"id","line":29,"val":"interpolation"}
+{"type":"code","line":29,"val":"str + 'interpolated'","escape":true,"buffer":true}
+{"type":"outdent","line":31}
+{"type":"code","line":31,"val":"var suffix = \"bar\"","escape":false,"buffer":false}
+{"type":"newline","line":32}
+{"type":"call","line":32,"val":"#{'foo' + suffix}","args":"'This is '"}
+{"type":"newline","line":33}
+{"type":"eos","line":33}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/namespaces.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/namespaces.expected.json
new file mode 100644
index 0000000..886bb7d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/namespaces.expected.json
@@ -0,0 +1,48 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "fb:user:role",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "Something",
+            "line": 1,
+            "filename": "namespaces.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "namespaces.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "fb:foo",
+          "val": "'bar'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "namespaces.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "namespaces.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/namespaces.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/namespaces.tokens.json
new file mode 100644
index 0000000..1c73230
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/namespaces.tokens.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"fb:user:role","selfClosing":false}
+{"type":"text","line":1,"val":"Something"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"foo","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"fb:foo","val":"'bar'","escaped":true}]}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/nesting.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/nesting.expected.json
new file mode 100644
index 0000000..44e8e12
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/nesting.expected.json
@@ -0,0 +1,157 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "a",
+                  "line": 2,
+                  "filename": "nesting.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "nesting.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "b",
+                  "line": 3,
+                  "filename": "nesting.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "nesting.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "ul",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "li",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "c",
+                              "line": 6,
+                              "filename": "nesting.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 6,
+                        "filename": "nesting.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "li",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Text",
+                              "val": "d",
+                              "line": 7,
+                              "filename": "nesting.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 7,
+                        "filename": "nesting.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 5,
+                  "filename": "nesting.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "nesting.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "e",
+                  "line": 8,
+                  "filename": "nesting.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "nesting.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "nesting.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "nesting.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/nesting.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/nesting.tokens.json
new file mode 100644
index 0000000..b160663
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/nesting.tokens.json
@@ -0,0 +1,23 @@
+{"type":"tag","line":1,"val":"ul","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"li","selfClosing":false}
+{"type":"text","line":2,"val":"a"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"li","selfClosing":false}
+{"type":"text","line":3,"val":"b"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"li","selfClosing":false}
+{"type":"indent","line":5,"val":4}
+{"type":"tag","line":5,"val":"ul","selfClosing":false}
+{"type":"indent","line":6,"val":8}
+{"type":"tag","line":6,"val":"li","selfClosing":false}
+{"type":"text","line":6,"val":"c"}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"d"}
+{"type":"outdent","line":8}
+{"type":"outdent","line":8}
+{"type":"tag","line":8,"val":"li","selfClosing":false}
+{"type":"text","line":8,"val":"e"}
+{"type":"outdent","line":8}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/pre.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/pre.expected.json
new file mode 100644
index 0000000..7b27893
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/pre.expected.json
@@ -0,0 +1,114 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "pre",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "foo",
+            "line": 2
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "bar",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 4
+          },
+          {
+            "type": "Text",
+            "val": "baz",
+            "line": 4
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 5
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 5
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "pre.tokens.json",
+      "textOnly": true
+    },
+    {
+      "type": "Tag",
+      "name": "pre",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "code",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "foo",
+                  "line": 8
+                },
+                {
+                  "type": "Text",
+                  "val": "\n",
+                  "line": 9
+                },
+                {
+                  "type": "Text",
+                  "val": "bar",
+                  "line": 9
+                },
+                {
+                  "type": "Text",
+                  "val": "\n",
+                  "line": 10
+                },
+                {
+                  "type": "Text",
+                  "val": "baz",
+                  "line": 10
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": true,
+            "line": 7,
+            "filename": "pre.tokens.json",
+            "textOnly": true
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "pre.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "pre.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/pre.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/pre.tokens.json
new file mode 100644
index 0000000..c019cc4
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/pre.tokens.json
@@ -0,0 +1,25 @@
+{"type":"tag","line":1,"val":"pre","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"foo"}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"bar"}
+{"type":"newline","line":4}
+{"type":"text","line":4,"val":"baz"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"end-pipeless-text","line":5}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"pre","selfClosing":false}
+{"type":"indent","line":7,"val":2}
+{"type":"tag","line":7,"val":"code","selfClosing":false}
+{"type":"dot","line":7}
+{"type":"start-pipeless-text","line":7}
+{"type":"text","line":8,"val":"foo"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"bar"}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":"baz"}
+{"type":"end-pipeless-text","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/quotes.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/quotes.expected.json
new file mode 100644
index 0000000..0df3a3c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/quotes.expected.json
@@ -0,0 +1,49 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "\"foo\"",
+            "line": 1,
+            "filename": "quotes.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "quotes.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "'foo'",
+            "line": 2,
+            "filename": "quotes.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "quotes.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "quotes.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/quotes.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/quotes.tokens.json
new file mode 100644
index 0000000..c4ba8ac
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/quotes.tokens.json
@@ -0,0 +1,6 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"\"foo\""}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"p","selfClosing":false}
+{"type":"text","line":2,"val":"'foo'"}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/regression.784.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/regression.784.expected.json
new file mode 100644
index 0000000..bc683fe
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/regression.784.expected.json
@@ -0,0 +1,41 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var url = 'http://www.google.com'",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "regression.784.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "#{url.replace('http://', '').replace(/^www\\./, '')}",
+            "line": 2,
+            "filename": "regression.784.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "'url'",
+          "escaped": false
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "regression.784.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "regression.784.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/regression.784.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/regression.784.tokens.json
new file mode 100644
index 0000000..ed9581d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/regression.784.tokens.json
@@ -0,0 +1,5 @@
+{"type":"code","line":1,"val":"var url = 'http://www.google.com'","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"class","line":2,"val":"url"}
+{"type":"text","line":2,"val":"#{url.replace('http://', '').replace(/^www\\./, '')}"}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/script.whitespace.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/script.whitespace.expected.json
new file mode 100644
index 0000000..4dcfffe
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/script.whitespace.expected.json
@@ -0,0 +1,68 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "if (foo) {",
+            "line": 2
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "  ",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 4
+          },
+          {
+            "type": "Text",
+            "val": "  bar();",
+            "line": 4
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 5
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 5
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 6
+          },
+          {
+            "type": "Text",
+            "val": "}",
+            "line": 6
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "script.whitespace.tokens.json",
+      "textOnly": true
+    }
+  ],
+  "line": 0,
+  "filename": "script.whitespace.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/script.whitespace.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/script.whitespace.tokens.json
new file mode 100644
index 0000000..65105d5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/script.whitespace.tokens.json
@@ -0,0 +1,14 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"if (foo) {"}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"  "}
+{"type":"newline","line":4}
+{"type":"text","line":4,"val":"  bar();"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"}"}
+{"type":"end-pipeless-text","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.expected.json
new file mode 100644
index 0000000..13dd3c1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.expected.json
@@ -0,0 +1,119 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "if (foo) {",
+            "line": 2
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "  bar();",
+            "line": 3
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 4
+          },
+          {
+            "type": "Text",
+            "val": "}",
+            "line": 4
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "scripts.tokens.json",
+      "textOnly": true
+    },
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "scripts.tokens.json",
+      "code": {
+        "type": "Code",
+        "val": "'foo()'",
+        "buffer": true,
+        "escape": false,
+        "line": 5,
+        "filename": "scripts.tokens.json"
+      }
+    },
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "foo()",
+            "line": 6,
+            "filename": "scripts.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "scripts.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 7,
+      "filename": "scripts.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "div",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 8,
+      "filename": "scripts.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "scripts.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.non-js.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.non-js.expected.json
new file mode 100644
index 0000000..aa861ce
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.non-js.expected.json
@@ -0,0 +1,147 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "div",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h1",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "<%= user.name %>",
+                        "line": 3,
+                        "filename": "scripts.non-js.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "scripts.non-js.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "<%= user.description %>",
+                        "line": 4,
+                        "filename": "scripts.non-js.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "scripts.non-js.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "id",
+                "val": "'user'",
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "scripts.non-js.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "id",
+          "val": "'user-template'",
+          "escaped": false
+        },
+        {
+          "name": "type",
+          "val": "'text/template'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "scripts.non-js.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "if (foo) {",
+            "line": 7
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 8
+          },
+          {
+            "type": "Text",
+            "val": "  bar();",
+            "line": 8
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 9
+          },
+          {
+            "type": "Text",
+            "val": "}",
+            "line": 9
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "id",
+          "val": "'user-template'",
+          "escaped": false
+        },
+        {
+          "name": "type",
+          "val": "'text/template'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "scripts.non-js.tokens.json",
+      "textOnly": true
+    }
+  ],
+  "line": 0,
+  "filename": "scripts.non-js.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.non-js.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.non-js.tokens.json
new file mode 100644
index 0000000..d28fc21
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.non-js.tokens.json
@@ -0,0 +1,25 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"id","line":1,"val":"user-template"}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"id","line":2,"val":"user"}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h1","selfClosing":false}
+{"type":"text","line":3,"val":"<%= user.name %>"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"<%= user.description %>"}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"id","line":6,"val":"user-template"}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'text/template'","escaped":true}]}
+{"type":"dot","line":6}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"if (foo) {"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"  bar();"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"}"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.tokens.json
new file mode 100644
index 0000000..ee2753b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/scripts.tokens.json
@@ -0,0 +1,20 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"dot","line":1}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"if (foo) {"}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":"  bar();"}
+{"type":"newline","line":4}
+{"type":"text","line":4,"val":"}"}
+{"type":"end-pipeless-text","line":4}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"code","line":5,"val":"'foo()'","escape":false,"buffer":true}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"text","line":6,"val":"foo()"}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"script","selfClosing":false}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"div","selfClosing":false}
+{"type":"eos","line":8}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/single-period.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/single-period.expected.json
new file mode 100644
index 0000000..0af2d85
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/single-period.expected.json
@@ -0,0 +1,28 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "span",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": ".",
+            "line": 1,
+            "filename": "single-period.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 1,
+      "filename": "single-period.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "single-period.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/single-period.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/single-period.tokens.json
new file mode 100644
index 0000000..c776758
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/single-period.tokens.json
@@ -0,0 +1,3 @@
+{"type":"tag","line":1,"val":"span","selfClosing":false}
+{"type":"text","line":1,"val":"."}
+{"type":"eos","line":1}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/source.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/source.expected.json
new file mode 100644
index 0000000..2c37c59
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/source.expected.json
@@ -0,0 +1,93 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "audio",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "source",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "src",
+                      "val": "'foo'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "source.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "source",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "src",
+                      "val": "'bar'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "source.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "preload",
+                "val": "'auto'",
+                "escaped": true
+              },
+              {
+                "name": "autobuffer",
+                "val": true,
+                "escaped": false
+              },
+              {
+                "name": "controls",
+                "val": true,
+                "escaped": false
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "source.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "source.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "source.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/source.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/source.tokens.json
new file mode 100644
index 0000000..97f3119
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/source.tokens.json
@@ -0,0 +1,13 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"audio","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"preload","val":"'auto'","escaped":true},{"name":"autobuffer","val":true,"escaped":false},{"name":"controls","val":true,"escaped":false}]}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"source","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"src","val":"'foo'","escaped":true}]}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"source","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'bar'","escaped":true}]}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/styles.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/styles.expected.json
new file mode 100644
index 0000000..77afb1a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/styles.expected.json
@@ -0,0 +1,288 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "head",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "style",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "body {",
+                        "line": 4
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 5
+                      },
+                      {
+                        "type": "Text",
+                        "val": "  padding: 50px;",
+                        "line": 5
+                      },
+                      {
+                        "type": "Text",
+                        "val": "\n",
+                        "line": 6
+                      },
+                      {
+                        "type": "Text",
+                        "val": "}",
+                        "line": 6
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "styles.tokens.json",
+                  "textOnly": true
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "styles.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "style",
+                      "val": "'color:red;background:green'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 8,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "style",
+                      "val": "{color: 'red', background: 'green'}",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 9,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [
+                    "{style: 'color:red;background:green'}"
+                  ],
+                  "isInline": false,
+                  "line": 10,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [
+                    "{style: {color: 'red', background: 'green'}}"
+                  ],
+                  "isInline": false,
+                  "line": 11,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Mixin",
+                  "name": "div",
+                  "args": null,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "div",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [
+                          "attributes"
+                        ],
+                        "isInline": false,
+                        "line": 13,
+                        "filename": "styles.tokens.json"
+                      }
+                    ],
+                    "line": 13,
+                    "filename": "styles.tokens.json"
+                  },
+                  "call": false,
+                  "line": 12,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Mixin",
+                  "name": "div",
+                  "block": null,
+                  "call": true,
+                  "attrs": [
+                    {
+                      "name": "style",
+                      "val": "'color:red;background:green'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "line": 14,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Mixin",
+                  "name": "div",
+                  "block": null,
+                  "call": true,
+                  "attrs": [
+                    {
+                      "name": "style",
+                      "val": "{color: 'red', background: 'green'}",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "line": 15,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Code",
+                  "val": "var bg = 'green';",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 16,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "style",
+                      "val": "{color: 'red', background: bg}",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 17,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "div",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [
+                    "{style: {color: 'red', background: bg}}"
+                  ],
+                  "isInline": false,
+                  "line": 18,
+                  "filename": "styles.tokens.json"
+                },
+                {
+                  "type": "Mixin",
+                  "name": "div",
+                  "block": null,
+                  "call": true,
+                  "attrs": [
+                    {
+                      "name": "style",
+                      "val": "{color: 'red', background: bg}",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "line": 19,
+                  "filename": "styles.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 7,
+            "filename": "styles.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "styles.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "styles.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/styles.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/styles.tokens.json
new file mode 100644
index 0000000..4f222b5
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/styles.tokens.json
@@ -0,0 +1,52 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"head","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"style","selfClosing":false}
+{"type":"dot","line":3}
+{"type":"start-pipeless-text","line":3}
+{"type":"text","line":4,"val":"body {"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"  padding: 50px;"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"}"}
+{"type":"end-pipeless-text","line":6}
+{"type":"outdent","line":7}
+{"type":"tag","line":7,"val":"body","selfClosing":false}
+{"type":"indent","line":8,"val":4}
+{"type":"tag","line":8,"val":"div","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"style","val":"'color:red;background:green'","escaped":true}]}
+{"type":"newline","line":9}
+{"type":"tag","line":9,"val":"div","selfClosing":false}
+{"type":"attrs","line":9,"attrs":[{"name":"style","val":"{color: 'red', background: 'green'}","escaped":true}]}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"div","selfClosing":false}
+{"type":"&attributes","line":10,"val":"{style: 'color:red;background:green'}"}
+{"type":"newline","line":11}
+{"type":"tag","line":11,"val":"div","selfClosing":false}
+{"type":"&attributes","line":11,"val":"{style: {color: 'red', background: 'green'}}"}
+{"type":"newline","line":12}
+{"type":"mixin","line":12,"val":"div","args":null}
+{"type":"indent","line":13,"val":6}
+{"type":"tag","line":13,"val":"div","selfClosing":false}
+{"type":"&attributes","line":13,"val":"attributes"}
+{"type":"outdent","line":14}
+{"type":"call","line":14,"val":"div"}
+{"type":"attrs","line":14,"attrs":[{"name":"style","val":"'color:red;background:green'","escaped":true}]}
+{"type":"newline","line":15}
+{"type":"call","line":15,"val":"div"}
+{"type":"attrs","line":15,"attrs":[{"name":"style","val":"{color: 'red', background: 'green'}","escaped":true}]}
+{"type":"newline","line":16}
+{"type":"code","line":16,"val":"var bg = 'green';","escape":false,"buffer":false}
+{"type":"newline","line":17}
+{"type":"tag","line":17,"val":"div","selfClosing":false}
+{"type":"attrs","line":17,"attrs":[{"name":"style","val":"{color: 'red', background: bg}","escaped":true}]}
+{"type":"newline","line":18}
+{"type":"tag","line":18,"val":"div","selfClosing":false}
+{"type":"&attributes","line":18,"val":"{style: {color: 'red', background: bg}}"}
+{"type":"newline","line":19}
+{"type":"call","line":19,"val":"div"}
+{"type":"attrs","line":19,"attrs":[{"name":"style","val":"{color: 'red', background: bg}","escaped":true}]}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"eos","line":20}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tag.interpolation.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tag.interpolation.expected.json
new file mode 100644
index 0000000..1e75aef
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tag.interpolation.expected.json
@@ -0,0 +1,315 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var tag = 'p'",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "tag.interpolation.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var foo = 'bar'",
+      "buffer": false,
+      "escape": false,
+      "line": 3,
+      "filename": "tag.interpolation.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "tag",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "value",
+            "line": 5,
+            "filename": "tag.interpolation.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "buffer": true,
+      "isInline": false,
+      "line": 5,
+      "filename": "tag.interpolation.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "tag",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "value",
+            "line": 6,
+            "filename": "tag.interpolation.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "foo",
+          "val": "'bar'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "buffer": true,
+      "isInline": false,
+      "line": 6,
+      "filename": "tag.interpolation.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "foo ? 'a' : 'li'",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "here",
+            "line": 7,
+            "filename": "tag.interpolation.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "something",
+          "val": true,
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "buffer": true,
+      "isInline": false,
+      "line": 7,
+      "filename": "tag.interpolation.tokens.json"
+    },
+    {
+      "type": "Mixin",
+      "name": "item",
+      "args": "icon",
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "li",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Code",
+                  "val": "if ( attributes.href)",
+                  "line": 11,
+                  "filename": "tag.interpolation.tokens.json",
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "a",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "img",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": []
+                              },
+                              "attrs": [
+                                {
+                                  "name": "class",
+                                  "val": "'icon'",
+                                  "escaped": false
+                                },
+                                {
+                                  "name": "src",
+                                  "val": "icon",
+                                  "escaped": true
+                                }
+                              ],
+                              "attributeBlocks": [],
+                              "isInline": true,
+                              "line": 13,
+                              "filename": "tag.interpolation.tokens.json"
+                            },
+                            {
+                              "type": "MixinBlock",
+                              "line": 14,
+                              "filename": "tag.interpolation.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [
+                          "attributes"
+                        ],
+                        "isInline": true,
+                        "line": 12,
+                        "filename": "tag.interpolation.tokens.json"
+                      }
+                    ],
+                    "line": 12,
+                    "filename": "tag.interpolation.tokens.json"
+                  }
+                },
+                {
+                  "type": "Code",
+                  "val": "else",
+                  "line": 15,
+                  "filename": "tag.interpolation.tokens.json",
+                  "debug": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "span",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": [
+                            {
+                              "type": "Tag",
+                              "name": "img",
+                              "selfClosing": false,
+                              "block": {
+                                "type": "Block",
+                                "nodes": []
+                              },
+                              "attrs": [
+                                {
+                                  "name": "class",
+                                  "val": "'icon'",
+                                  "escaped": false
+                                },
+                                {
+                                  "name": "src",
+                                  "val": "icon",
+                                  "escaped": true
+                                }
+                              ],
+                              "attributeBlocks": [],
+                              "isInline": true,
+                              "line": 17,
+                              "filename": "tag.interpolation.tokens.json"
+                            },
+                            {
+                              "type": "MixinBlock",
+                              "line": 18,
+                              "filename": "tag.interpolation.tokens.json"
+                            }
+                          ]
+                        },
+                        "attrs": [],
+                        "attributeBlocks": [
+                          "attributes"
+                        ],
+                        "isInline": true,
+                        "line": 16,
+                        "filename": "tag.interpolation.tokens.json"
+                      }
+                    ],
+                    "line": 16,
+                    "filename": "tag.interpolation.tokens.json"
+                  }
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 10,
+            "filename": "tag.interpolation.tokens.json"
+          }
+        ],
+        "line": 10,
+        "filename": "tag.interpolation.tokens.json"
+      },
+      "call": false,
+      "line": 9,
+      "filename": "tag.interpolation.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Mixin",
+            "name": "item",
+            "args": "'contact'",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Contact",
+                  "line": 21,
+                  "filename": "tag.interpolation.tokens.json"
+                }
+              ],
+              "line": 21,
+              "filename": "tag.interpolation.tokens.json"
+            },
+            "call": true,
+            "attrs": [],
+            "attributeBlocks": [],
+            "line": 21,
+            "filename": "tag.interpolation.tokens.json"
+          },
+          {
+            "type": "Mixin",
+            "name": "item",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "Contact",
+                  "line": 22,
+                  "filename": "tag.interpolation.tokens.json"
+                }
+              ],
+              "line": 22,
+              "filename": "tag.interpolation.tokens.json"
+            },
+            "call": true,
+            "attrs": [
+              {
+                "name": "href",
+                "val": "'/contact'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "line": 22,
+            "filename": "tag.interpolation.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 20,
+      "filename": "tag.interpolation.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "tag.interpolation.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tag.interpolation.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tag.interpolation.tokens.json
new file mode 100644
index 0000000..e431532
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tag.interpolation.tokens.json
@@ -0,0 +1,56 @@
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var tag = 'p'","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"code","line":3,"val":"var foo = 'bar'","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"interpolation","line":5,"val":"tag"}
+{"type":"text","line":5,"val":"value"}
+{"type":"newline","line":6}
+{"type":"interpolation","line":6,"val":"tag"}
+{"type":"attrs","line":6,"attrs":[{"name":"foo","val":"'bar'","escaped":true}]}
+{"type":"text","line":6,"val":"value"}
+{"type":"newline","line":7}
+{"type":"interpolation","line":7,"val":"foo ? 'a' : 'li'"}
+{"type":"attrs","line":7,"attrs":[{"name":"something","val":true,"escaped":true}]}
+{"type":"text","line":7,"val":"here"}
+{"type":"newline","line":9}
+{"type":"mixin","line":9,"val":"item","args":"icon"}
+{"type":"indent","line":10,"val":2}
+{"type":"tag","line":10,"val":"li","selfClosing":false}
+{"type":"indent","line":11,"val":4}
+{"type":"code","line":11,"val":"if ( attributes.href)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":12,"val":6}
+{"type":"tag","line":12,"val":"a","selfClosing":false}
+{"type":"&attributes","line":12,"val":"attributes"}
+{"type":"indent","line":13,"val":8}
+{"type":"tag","line":13,"val":"img","selfClosing":false}
+{"type":"class","line":13,"val":"icon"}
+{"type":"attrs","line":13,"attrs":[{"name":"src","val":"icon","escaped":true}]}
+{"type":"newline","line":14}
+{"type":"mixin-block","line":14}
+{"type":"outdent","line":15}
+{"type":"outdent","line":15}
+{"type":"code","line":15,"val":"else","isElse":true,"isIf":false,"requiresBlock":true}
+{"type":"indent","line":16,"val":6}
+{"type":"tag","line":16,"val":"span","selfClosing":false}
+{"type":"&attributes","line":16,"val":"attributes"}
+{"type":"indent","line":17,"val":8}
+{"type":"tag","line":17,"val":"img","selfClosing":false}
+{"type":"class","line":17,"val":"icon"}
+{"type":"attrs","line":17,"attrs":[{"name":"src","val":"icon","escaped":true}]}
+{"type":"newline","line":18}
+{"type":"mixin-block","line":18}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"outdent","line":20}
+{"type":"tag","line":20,"val":"ul","selfClosing":false}
+{"type":"indent","line":21,"val":2}
+{"type":"call","line":21,"val":"item","args":"'contact'"}
+{"type":"text","line":21,"val":"Contact"}
+{"type":"newline","line":22}
+{"type":"call","line":22,"val":"item"}
+{"type":"attrs","line":22,"attrs":[{"name":"href","val":"'/contact'","escaped":true}]}
+{"type":"text","line":22,"val":"Contact"}
+{"type":"outdent","line":23}
+{"type":"eos","line":23}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tags.self-closing.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tags.self-closing.expected.json
new file mode 100644
index 0000000..acacc74
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tags.self-closing.expected.json
@@ -0,0 +1,194 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "body",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "foo",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "foo",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "bar",
+                "val": "'baz'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "foo",
+            "selfClosing": true,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 5,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "foo",
+            "selfClosing": true,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "bar",
+                "val": "'baz'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 6,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "foo",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "/",
+                  "line": 7,
+                  "filename": "tags.self-closing.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 7,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "foo",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "/",
+                  "line": 8,
+                  "filename": "tags.self-closing.tokens.json"
+                }
+              ]
+            },
+            "attrs": [
+              {
+                "name": "bar",
+                "val": "'baz'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 8,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Comment",
+            "val": " can have a single space after them",
+            "buffer": false,
+            "line": 9,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "img",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": " ",
+                  "line": 10,
+                  "filename": "tags.self-closing.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": true,
+            "line": 10,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Comment",
+            "val": " can have lots of white space after them",
+            "buffer": false,
+            "line": 11,
+            "filename": "tags.self-closing.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "img",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Text",
+                  "val": "   ",
+                  "line": 12,
+                  "filename": "tags.self-closing.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": true,
+            "line": 12,
+            "filename": "tags.self-closing.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "tags.self-closing.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "tags.self-closing.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tags.self-closing.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tags.self-closing.tokens.json
new file mode 100644
index 0000000..e2ae701
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/tags.self-closing.tokens.json
@@ -0,0 +1,31 @@
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"foo","selfClosing":false}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"foo","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"bar","val":"'baz'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"foo","selfClosing":true}
+{"type":"newline","line":6}
+{"type":"tag","line":6,"val":"foo","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"bar","val":"'baz'","escaped":true}],"selfClosing":true}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"foo","selfClosing":false}
+{"type":"text","line":7,"val":"/"}
+{"type":"newline","line":8}
+{"type":"tag","line":8,"val":"foo","selfClosing":false}
+{"type":"attrs","line":8,"attrs":[{"name":"bar","val":"'baz'","escaped":true}]}
+{"type":"text","line":8,"val":"/"}
+{"type":"newline","line":9}
+{"type":"comment","line":9,"val":" can have a single space after them","buffer":false}
+{"type":"newline","line":10}
+{"type":"tag","line":10,"val":"img","selfClosing":false}
+{"type":"text","line":10,"val":" "}
+{"type":"newline","line":11}
+{"type":"comment","line":11,"val":" can have lots of white space after them","buffer":false}
+{"type":"newline","line":12}
+{"type":"tag","line":12,"val":"img","selfClosing":false}
+{"type":"text","line":12,"val":"   "}
+{"type":"outdent","line":12}
+{"type":"eos","line":12}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/template.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/template.expected.json
new file mode 100644
index 0000000..682a435
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/template.expected.json
@@ -0,0 +1,132 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "article",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "h2",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "{{title}}",
+                        "line": 3,
+                        "filename": "template.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 3,
+                  "filename": "template.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "p",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "{{description}}",
+                        "line": 4,
+                        "filename": "template.tokens.json"
+                      }
+                    ]
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 4,
+                  "filename": "template.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "template.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'text/x-template'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "template.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "script",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "article",
+            "line": 7
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 8
+          },
+          {
+            "type": "Text",
+            "val": "  h2 {{title}}",
+            "line": 8
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 9
+          },
+          {
+            "type": "Text",
+            "val": "  p {{description}}",
+            "line": 9
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "type",
+          "val": "'text/x-template'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 6,
+      "filename": "template.tokens.json",
+      "textOnly": true
+    }
+  ],
+  "line": 0,
+  "filename": "template.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/template.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/template.tokens.json
new file mode 100644
index 0000000..24b2346
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/template.tokens.json
@@ -0,0 +1,23 @@
+{"type":"tag","line":1,"val":"script","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"article","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"tag","line":3,"val":"h2","selfClosing":false}
+{"type":"text","line":3,"val":"{{title}}"}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"p","selfClosing":false}
+{"type":"text","line":4,"val":"{{description}}"}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"tag","line":6,"val":"script","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'text/x-template'","escaped":true}]}
+{"type":"dot","line":6}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"article"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"  h2 {{title}}"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"  p {{description}}"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text-block.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text-block.expected.json
new file mode 100644
index 0000000..2407126
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text-block.expected.json
@@ -0,0 +1,99 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "label",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "Username:",
+            "line": 2,
+            "filename": "text-block.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "input",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "type",
+                "val": "'text'",
+                "escaped": true
+              },
+              {
+                "name": "name",
+                "val": "'user[name]'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 3,
+            "filename": "text-block.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "text-block.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "label",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "Password:",
+            "line": 5,
+            "filename": "text-block.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "input",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "type",
+                "val": "'text'",
+                "escaped": true
+              },
+              {
+                "name": "name",
+                "val": "'user[pass]'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 6,
+            "filename": "text-block.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 5,
+      "filename": "text-block.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "text-block.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text-block.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text-block.tokens.json
new file mode 100644
index 0000000..65f7365
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text-block.tokens.json
@@ -0,0 +1,14 @@
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"label","selfClosing":false}
+{"type":"text","line":2,"val":"Username:"}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"input","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'user[name]'","escaped":true}]}
+{"type":"outdent","line":5}
+{"type":"tag","line":5,"val":"label","selfClosing":false}
+{"type":"text","line":5,"val":"Password:"}
+{"type":"indent","line":6,"val":2}
+{"type":"tag","line":6,"val":"input","selfClosing":false}
+{"type":"attrs","line":6,"attrs":[{"name":"type","val":"'text'","escaped":true},{"name":"name","val":"'user[pass]'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text.expected.json
new file mode 100644
index 0000000..4c6f8c2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text.expected.json
@@ -0,0 +1,272 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "option",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "-- (selected) --",
+            "line": 1,
+            "filename": "text.tokens.json"
+          }
+        ]
+      },
+      "attrs": [
+        {
+          "name": "value",
+          "val": "''",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "text.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "foo",
+                "line": 4,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 5,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "bar",
+                "line": 5,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 6,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "baz",
+                "line": 6,
+                "filename": "text.tokens.json"
+              }
+            ],
+            "line": 4,
+            "filename": "text.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "text.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "foo",
+            "line": 9
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 10
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 10
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 11
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 11
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 12
+          },
+          {
+            "type": "Text",
+            "val": "bar",
+            "line": 12
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 13
+          },
+          {
+            "type": "Text",
+            "val": "baz",
+            "line": 13
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 14
+          },
+          {
+            "type": "Text",
+            "val": "",
+            "line": 14
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 8,
+      "filename": "text.tokens.json",
+      "textOnly": true
+    },
+    {
+      "type": "Tag",
+      "name": "pre",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Block",
+            "nodes": [
+              {
+                "type": "Text",
+                "val": "foo",
+                "line": 16,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 17,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "  bar",
+                "line": 17,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 18,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "    baz",
+                "line": 18,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": "\n",
+                "line": 19,
+                "filename": "text.tokens.json"
+              },
+              {
+                "type": "Text",
+                "val": ".",
+                "line": 19,
+                "filename": "text.tokens.json"
+              }
+            ],
+            "line": 16,
+            "filename": "text.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 15,
+      "filename": "text.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "pre",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "foo",
+            "line": 22
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 23
+          },
+          {
+            "type": "Text",
+            "val": "  bar",
+            "line": 23
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 24
+          },
+          {
+            "type": "Text",
+            "val": "    baz",
+            "line": 24
+          },
+          {
+            "type": "Text",
+            "val": "\n",
+            "line": 25
+          },
+          {
+            "type": "Text",
+            "val": ".",
+            "line": 25
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 21,
+      "filename": "text.tokens.json",
+      "textOnly": true
+    }
+  ],
+  "line": 0,
+  "filename": "text.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text.tokens.json
new file mode 100644
index 0000000..ddc2a06
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/text.tokens.json
@@ -0,0 +1,50 @@
+{"type":"tag","line":1,"val":"option","selfClosing":false}
+{"type":"attrs","line":1,"attrs":[{"name":"value","val":"''","escaped":true}]}
+{"type":"text","line":1,"val":"-- (selected) --"}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"p","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"text","line":4,"val":"foo"}
+{"type":"newline","line":5}
+{"type":"text","line":5,"val":"bar"}
+{"type":"newline","line":6}
+{"type":"text","line":6,"val":"baz"}
+{"type":"outdent","line":8}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"dot","line":8}
+{"type":"start-pipeless-text","line":8}
+{"type":"text","line":9,"val":"foo"}
+{"type":"newline","line":10}
+{"type":"text","line":10,"val":""}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":""}
+{"type":"newline","line":12}
+{"type":"text","line":12,"val":"bar"}
+{"type":"newline","line":13}
+{"type":"text","line":13,"val":"baz"}
+{"type":"newline","line":14}
+{"type":"text","line":14,"val":""}
+{"type":"end-pipeless-text","line":14}
+{"type":"newline","line":15}
+{"type":"tag","line":15,"val":"pre","selfClosing":false}
+{"type":"indent","line":16,"val":2}
+{"type":"text","line":16,"val":"foo"}
+{"type":"newline","line":17}
+{"type":"text","line":17,"val":"  bar"}
+{"type":"newline","line":18}
+{"type":"text","line":18,"val":"    baz"}
+{"type":"newline","line":19}
+{"type":"text","line":19,"val":"."}
+{"type":"outdent","line":21}
+{"type":"tag","line":21,"val":"pre","selfClosing":false}
+{"type":"dot","line":21}
+{"type":"start-pipeless-text","line":21}
+{"type":"text","line":22,"val":"foo"}
+{"type":"newline","line":23}
+{"type":"text","line":23,"val":"  bar"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"    baz"}
+{"type":"newline","line":25}
+{"type":"text","line":25,"val":"."}
+{"type":"end-pipeless-text","line":25}
+{"type":"eos","line":25}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/utf8bom.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/utf8bom.expected.json
new file mode 100644
index 0000000..3c98513
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/utf8bom.expected.json
@@ -0,0 +1,28 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "p",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "\"foo\"",
+            "line": 1,
+            "filename": "utf8bom.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "utf8bom.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "utf8bom.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/utf8bom.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/utf8bom.tokens.json
new file mode 100644
index 0000000..90f8d9c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/utf8bom.tokens.json
@@ -0,0 +1,4 @@
+{"type":"tag","line":1,"val":"p","selfClosing":false}
+{"type":"text","line":1,"val":"\"foo\""}
+{"type":"newline","line":2}
+{"type":"eos","line":2}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/vars.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/vars.expected.json
new file mode 100644
index 0000000..1088d8a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/vars.expected.json
@@ -0,0 +1,48 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var foo = 'bar'",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "vars.tokens.json"
+    },
+    {
+      "type": "Code",
+      "val": "var list = [1,2,3]",
+      "buffer": false,
+      "escape": false,
+      "line": 2,
+      "filename": "vars.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "a",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "class",
+          "val": "list",
+          "escaped": true
+        },
+        {
+          "name": "id",
+          "val": "foo",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": true,
+      "line": 3,
+      "filename": "vars.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "vars.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/vars.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/vars.tokens.json
new file mode 100644
index 0000000..df2aec1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/vars.tokens.json
@@ -0,0 +1,7 @@
+{"type":"code","line":1,"val":"var foo = 'bar'","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"code","line":2,"val":"var list = [1,2,3]","escape":false,"buffer":false}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"a","selfClosing":false}
+{"type":"attrs","line":3,"attrs":[{"name":"class","val":"list","escaped":true},{"name":"id","val":"foo","escaped":true}]}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/while.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/while.expected.json
new file mode 100644
index 0000000..1ec99bb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/while.expected.json
@@ -0,0 +1,73 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Code",
+      "val": "var x = 1;",
+      "buffer": false,
+      "escape": false,
+      "line": 1,
+      "filename": "while.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "ul",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Code",
+            "val": "while (x < 10)",
+            "line": 3,
+            "filename": "while.tokens.json",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Code",
+                  "val": "x++;",
+                  "buffer": false,
+                  "escape": false,
+                  "line": 4,
+                  "filename": "while.tokens.json"
+                },
+                {
+                  "type": "Tag",
+                  "name": "li",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 5,
+                  "filename": "while.tokens.json",
+                  "code": {
+                    "type": "Code",
+                    "val": "x",
+                    "buffer": true,
+                    "escape": true,
+                    "line": 5,
+                    "filename": "while.tokens.json"
+                  }
+                }
+              ],
+              "line": 4,
+              "filename": "while.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "while.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "while.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/while.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/while.tokens.json
new file mode 100644
index 0000000..5b3625d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/while.tokens.json
@@ -0,0 +1,13 @@
+{"type":"code","line":1,"val":"var x = 1;","escape":false,"buffer":false}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"ul","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"code","line":3,"val":"while (x < 10)","requiresBlock":true}
+{"type":"indent","line":4,"val":4}
+{"type":"code","line":4,"val":"x++;","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"code","line":5,"val":"x","escape":true,"buffer":true}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/xml.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/xml.expected.json
new file mode 100644
index 0000000..2a2959e
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/xml.expected.json
@@ -0,0 +1,54 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Doctype",
+      "val": "xml",
+      "line": 1,
+      "filename": "xml.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "category",
+      "selfClosing": true,
+      "block": {
+        "type": "Block",
+        "nodes": []
+      },
+      "attrs": [
+        {
+          "name": "term",
+          "val": "'some term'",
+          "escaped": true
+        }
+      ],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 2,
+      "filename": "xml.tokens.json"
+    },
+    {
+      "type": "Tag",
+      "name": "link",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Text",
+            "val": "http://google.com",
+            "line": 3,
+            "filename": "xml.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 3,
+      "filename": "xml.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "xml.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/xml.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/xml.tokens.json
new file mode 100644
index 0000000..2ba3ad2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/xml.tokens.json
@@ -0,0 +1,8 @@
+{"type":"doctype","line":1,"val":"xml"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"category","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"term","val":"'some term'","escaped":true}],"selfClosing":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"link","selfClosing":false}
+{"type":"text","line":3,"val":"http://google.com"}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional-head.expected.json
new file mode 100644
index 0000000..7abb94b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional-head.expected.json
@@ -0,0 +1,82 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "head",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "src",
+                "val": "'/jquery.js'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "yield-before-conditional-head.tokens.json"
+          },
+          {
+            "type": "Block",
+            "nodes": [],
+            "line": 3,
+            "filename": "yield-before-conditional-head.tokens.json",
+            "yield": true
+          },
+          {
+            "type": "Code",
+            "val": "if ( false)",
+            "line": 4,
+            "filename": "yield-before-conditional-head.tokens.json",
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Tag",
+                  "name": "script",
+                  "selfClosing": false,
+                  "block": {
+                    "type": "Block",
+                    "nodes": []
+                  },
+                  "attrs": [
+                    {
+                      "name": "src",
+                      "val": "'/jquery.ui.js'",
+                      "escaped": true
+                    }
+                  ],
+                  "attributeBlocks": [],
+                  "isInline": false,
+                  "line": 5,
+                  "filename": "yield-before-conditional-head.tokens.json"
+                }
+              ],
+              "line": 5,
+              "filename": "yield-before-conditional-head.tokens.json"
+            }
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "yield-before-conditional-head.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "yield-before-conditional-head.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional-head.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional-head.tokens.json
new file mode 100644
index 0000000..a5a09dd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional-head.tokens.json
@@ -0,0 +1,14 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"script","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"src","val":"'/jquery.js'","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"yield","line":3}
+{"type":"newline","line":4}
+{"type":"code","line":4,"val":"if ( false)","isElse":false,"isIf":true,"requiresBlock":true}
+{"type":"indent","line":5,"val":4}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/jquery.ui.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional.expected.json
new file mode 100644
index 0000000..a0014a2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional.expected.json
@@ -0,0 +1,96 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Include",
+                  "file": {
+                    "type": "FileReference",
+                    "path": "yield-before-conditional-head",
+                    "line": 3,
+                    "filename": "yield-before-conditional.tokens.json"
+                  },
+                  "attrs": [],
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "script",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "src",
+                            "val": "'/caustic.js'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 4,
+                        "filename": "yield-before-conditional.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "script",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "src",
+                            "val": "'/app.js'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 5,
+                        "filename": "yield-before-conditional.tokens.json"
+                      }
+                    ],
+                    "line": 4,
+                    "filename": "yield-before-conditional.tokens.json"
+                  },
+                  "line": 3,
+                  "filename": "yield-before-conditional.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "yield-before-conditional.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "yield-before-conditional.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "yield-before-conditional.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional.tokens.json
new file mode 100644
index 0000000..17eb97d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-before-conditional.tokens.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"yield-before-conditional-head"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/caustic.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/app.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-head.expected.json
new file mode 100644
index 0000000..ea262eb
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-head.expected.json
@@ -0,0 +1,69 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "head",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "src",
+                "val": "'/jquery.js'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "yield-head.tokens.json"
+          },
+          {
+            "type": "Block",
+            "nodes": [],
+            "line": 3,
+            "filename": "yield-head.tokens.json",
+            "yield": true
+          },
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "src",
+                "val": "'/jquery.ui.js'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "yield-head.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "yield-head.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "yield-head.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-head.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-head.tokens.json
new file mode 100644
index 0000000..bda3202
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-head.tokens.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"script","selfClosing":false}
+{"type":"attrs","line":2,"attrs":[{"name":"src","val":"'/jquery.js'","escaped":true}]}
+{"type":"newline","line":3}
+{"type":"yield","line":3}
+{"type":"newline","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/jquery.ui.js'","escaped":true}]}
+{"type":"outdent","line":5}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title-head.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title-head.expected.json
new file mode 100644
index 0000000..4871e47
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title-head.expected.json
@@ -0,0 +1,84 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "head",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "title",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Block",
+                  "nodes": [],
+                  "line": 3,
+                  "filename": "yield-title-head.tokens.json",
+                  "yield": true
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "yield-title-head.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "src",
+                "val": "'/jquery.js'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 4,
+            "filename": "yield-title-head.tokens.json"
+          },
+          {
+            "type": "Tag",
+            "name": "script",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": []
+            },
+            "attrs": [
+              {
+                "name": "src",
+                "val": "'/jquery.ui.js'",
+                "escaped": true
+              }
+            ],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 5,
+            "filename": "yield-title-head.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "yield-title-head.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "yield-title-head.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title-head.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title-head.tokens.json
new file mode 100644
index 0000000..69f4114
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title-head.tokens.json
@@ -0,0 +1,13 @@
+{"type":"tag","line":1,"val":"head","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"title","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"yield","line":3}
+{"type":"outdent","line":4}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/jquery.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/jquery.ui.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title.expected.json
new file mode 100644
index 0000000..cc70fe3
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title.expected.json
@@ -0,0 +1,62 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Include",
+                  "file": {
+                    "type": "FileReference",
+                    "path": "yield-title-head",
+                    "line": 3,
+                    "filename": "yield-title.tokens.json"
+                  },
+                  "attrs": [],
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Text",
+                        "val": "My Title",
+                        "line": 4,
+                        "filename": "yield-title.tokens.json"
+                      }
+                    ],
+                    "line": 4,
+                    "filename": "yield-title.tokens.json"
+                  },
+                  "line": 3,
+                  "filename": "yield-title.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "yield-title.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "yield-title.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "yield-title.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title.tokens.json
new file mode 100644
index 0000000..451c04d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield-title.tokens.json
@@ -0,0 +1,11 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"yield-title-head"}
+{"type":"indent","line":4,"val":6}
+{"type":"text","line":4,"val":"My Title"}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"outdent","line":4}
+{"type":"eos","line":4}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield.expected.json
new file mode 100644
index 0000000..746ab96
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield.expected.json
@@ -0,0 +1,96 @@
+{
+  "type": "Block",
+  "nodes": [
+    {
+      "type": "Tag",
+      "name": "html",
+      "selfClosing": false,
+      "block": {
+        "type": "Block",
+        "nodes": [
+          {
+            "type": "Tag",
+            "name": "body",
+            "selfClosing": false,
+            "block": {
+              "type": "Block",
+              "nodes": [
+                {
+                  "type": "Include",
+                  "file": {
+                    "type": "FileReference",
+                    "path": "yield-head",
+                    "line": 3,
+                    "filename": "yield.tokens.json"
+                  },
+                  "attrs": [],
+                  "block": {
+                    "type": "Block",
+                    "nodes": [
+                      {
+                        "type": "Tag",
+                        "name": "script",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "src",
+                            "val": "'/caustic.js'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 4,
+                        "filename": "yield.tokens.json"
+                      },
+                      {
+                        "type": "Tag",
+                        "name": "script",
+                        "selfClosing": false,
+                        "block": {
+                          "type": "Block",
+                          "nodes": []
+                        },
+                        "attrs": [
+                          {
+                            "name": "src",
+                            "val": "'/app.js'",
+                            "escaped": true
+                          }
+                        ],
+                        "attributeBlocks": [],
+                        "isInline": false,
+                        "line": 5,
+                        "filename": "yield.tokens.json"
+                      }
+                    ],
+                    "line": 4,
+                    "filename": "yield.tokens.json"
+                  },
+                  "line": 3,
+                  "filename": "yield.tokens.json"
+                }
+              ]
+            },
+            "attrs": [],
+            "attributeBlocks": [],
+            "isInline": false,
+            "line": 2,
+            "filename": "yield.tokens.json"
+          }
+        ]
+      },
+      "attrs": [],
+      "attributeBlocks": [],
+      "isInline": false,
+      "line": 1,
+      "filename": "yield.tokens.json"
+    }
+  ],
+  "line": 0,
+  "filename": "yield.tokens.json"
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield.tokens.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield.tokens.json
new file mode 100644
index 0000000..f88cba9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/cases/yield.tokens.json
@@ -0,0 +1,15 @@
+{"type":"tag","line":1,"val":"html","selfClosing":false}
+{"type":"indent","line":2,"val":2}
+{"type":"tag","line":2,"val":"body","selfClosing":false}
+{"type":"indent","line":3,"val":4}
+{"type":"include","line":3,"val":"yield-head"}
+{"type":"indent","line":4,"val":6}
+{"type":"tag","line":4,"val":"script","selfClosing":false}
+{"type":"attrs","line":4,"attrs":[{"name":"src","val":"'/caustic.js'","escaped":true}]}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"script","selfClosing":false}
+{"type":"attrs","line":5,"attrs":[{"name":"src","val":"'/app.js'","escaped":true}]}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"outdent","line":6}
+{"type":"eos","line":6}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/index.js
new file mode 100644
index 0000000..9b68e79
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/index.js
@@ -0,0 +1,31 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var getRepo = require('get-repo');
+var parse = require('../');
+
+var testCases = fs.readdirSync(__dirname + '/cases').filter(function (name) {
+  return /\.tokens\.json$/.test(name);
+});
+
+function parseNewlineJson(str) {
+  return str.split('\n').filter(Boolean).map(JSON.parse)
+}
+
+function read(path) {
+  return fs.readFileSync(__dirname + '/cases/' + path, 'utf8');
+}
+function write(path, body) {
+  return fs.writeFileSync(__dirname + '/cases/' + path, body);
+}
+
+testCases.forEach(function (filename) {
+  console.dir(filename);
+  var expectedAst = JSON.parse(read(filename.replace(/\.tokens\.json$/, '.expected.json')));
+  var actualAst = parse(parseNewlineJson(read(filename)), filename);
+  write(filename.replace(/\.tokens\.json$/, '.actual.json'), JSON.stringify(actualAst, null, '  '));
+  assert.deepEqual(actualAst, expectedAst);
+});
+
+console.log('tests passed');
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/update-test-cases.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/update-test-cases.js
new file mode 100644
index 0000000..ca541c9
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-parser/test/update-test-cases.js
@@ -0,0 +1,59 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var getRepo = require('get-repo');
+var parse = require('../');
+
+var existing = fs.readdirSync(__dirname + '/cases').filter(function (name) {
+  return /\.tokens\.json$/.test(name);
+});
+
+function parseNewlineJson(str) {
+  return str.split('\n').filter(Boolean).map(JSON.parse)
+}
+
+getRepo('jadejs', 'jade-lexer').on('data', function (entry) {
+  var match;
+  if (entry.type === 'File' && (match = /^\/test\/cases\/(.*)\.expected\.json$/.exec(entry.path))) {
+    var name = match[1];
+    var filename = name + '.tokens.json';
+    var alreadyExists = false;
+    existing = existing.filter(function (existingName) {
+      if (existingName === filename) {
+        alreadyExists = true;
+        return false;
+      }
+      return true;
+    });
+    if (alreadyExists) {
+      try {
+        var expectedTokens = parseNewlineJson(fs.readFileSync(__dirname + '/cases/' + filename, 'utf8'));
+        var actualTokens = parseNewlineJson(entry.body.toString('utf8'));
+        assert.deepEqual(actualTokens, expectedTokens);
+      } catch (ex) {
+        console.log('update: ' + filename);
+        fs.writeFileSync(__dirname + '/cases/' + filename, entry.body);
+      }
+      var actualAst = parse(parseNewlineJson(entry.body.toString('utf8')), filename);
+      try {
+        var expectedAst = JSON.parse(fs.readFileSync(__dirname + '/cases/' + name + '.expected.json', 'utf8'));
+        assert.deepEqual(actualAst, expectedAst);
+      } catch (ex) {
+        console.log('update: ' + name + '.expected.json');
+        fs.writeFileSync(__dirname + '/cases/' + name + '.expected.json', JSON.stringify(actualAst, null, '  '));
+      }
+    } else {
+      console.log('create: ' + filename);
+      fs.writeFileSync(__dirname + '/cases/' + filename, entry.body);
+      console.log('create: ' + name + '.expected.json');
+      var ast = parse(parseNewlineJson(entry.body.toString('utf8')), filename);
+      fs.writeFileSync(__dirname + '/cases/' + name + '.expected.json', JSON.stringify(ast, null, '  '));
+    }
+  }
+}).on('end', function () {
+  existing.forEach(function (file) {
+    fs.unlinkSync(__dirname + '/cases/' + file);
+  });
+  console.log('test cases updated');
+});
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/.npmignore b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/.npmignore
new file mode 100644
index 0000000..95566bc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/.npmignore
@@ -0,0 +1,15 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+results
+npm-debug.log
+node_modules
+*.actual.json
+coverage
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/.travis.yml b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/.travis.yml
new file mode 100644
index 0000000..9ccb608
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/.travis.yml
@@ -0,0 +1,6 @@
+sudo: false
+language: node_js
+node_js:
+  - "0.10"
+  - "0.12"
+  - "iojs"
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/CHANGELOG.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/CHANGELOG.md
new file mode 100644
index 0000000..15ece3b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## 1.0.0 - 2015-08-10
+### Added
+- Initial stable release – unchanged from 0.0.1
+
+## 0.0.1 - 2015-08-10
+### Added
+- Initial release
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/LICENSE.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/LICENSE.md
new file mode 100644
index 0000000..41403d2
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright (c) 2015 Tiancheng “Timothy” Gu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/README.md
new file mode 100644
index 0000000..5150ef0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/README.md
@@ -0,0 +1,45 @@
+# jade-strip-comments
+
+Strips comments from Jade token stream
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-strip-comments/master.svg)](https://travis-ci.org/jadejs/jade-strip-comments)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-strip-comments.svg)](https://gemnasium.com/jadejs/jade-strip-comments)
+[![NPM version](https://img.shields.io/npm/v/jade-strip-comments.svg)](https://www.npmjs.org/package/jade-strip-comments)
+
+## Installation
+
+    npm install jade-strip-comments
+
+## Usage
+
+```js
+var lex = require('jade-strip-comments');
+var stripComments = require('jade-strip-comments');
+
+var tokens = lex('//- unbuffered\n// buffered');
+// [ { type: 'comment', line: 1, val: ' unbuffered', buffer: false },
+//   { type: 'newline', line: 2 },
+//   { type: 'comment', line: 2, val: ' buffered', buffer: true },
+//   { type: 'eos', line: 2 } ]
+
+// Only strip unbuffered comments (default)
+stripComments(tokens, { filename: 'jade' });
+// [ { type: 'newline', line: 2 },
+//   { type: 'comment', line: 2, val: ' buffered', buffer: true },
+//   { type: 'eos', line: 2 } ]
+
+// Only strip buffered comments (when you want to play a joke on your coworkers)
+stripComments(tokens, { filename: 'jade', stripUnbuffered: false, stripBuffered: true });
+// [ { type: 'comment', line: 1, val: ' unbuffered', buffer: false },
+//   { type: 'newline', line: 2 },
+//   { type: 'eos', line: 2 } ]
+
+// Strip both (if you want Jade VERY clean)
+stripComments(tokens, { filename: 'jade', stripBuffered: true });
+// [ { type: 'newline', line: 2 },
+//   { type: 'eos', line: 2 } ]
+```
+
+## License
+
+MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/index.js
new file mode 100644
index 0000000..880a30c
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/index.js
@@ -0,0 +1,70 @@
+'use strict';
+
+var jadeError = require('jade-error');
+
+module.exports = stripComments;
+
+function unexpectedToken (type, occasion, filename, line) {
+  var msg = '`' + type + '` encountered when ' + occasion;
+  throw jadeError('UNEXPECTED_TOKEN', msg, { filename: filename, line: line });
+}
+
+function stripComments (input, options) {
+  options = options || {};
+
+  // Default: strip unbuffered comments and leave buffered ones alone
+  var stripUnbuffered = options.stripUnbuffered !== false;
+  var stripBuffered   = options.stripBuffered   === true;
+  var filename        = options.filename;
+
+  var out = [];
+  // If we have encountered a comment token and are not sure if we have gotten
+  // out of the comment or not
+  var inComment = false;
+  // If we are sure that we are in a block comment and all tokens except
+  // `end-pipeless-text` should be ignored
+  var inPipelessText = false;
+
+  return input.filter(function (tok) {
+    switch (tok.type) {
+      case 'comment':
+        if (inComment) {
+          unexpectedToken(
+            'comment', 'already in a comment', filename, tok.line
+          );
+        } else {
+          inComment = tok.buffer ? stripBuffered : stripUnbuffered;
+          return !inComment;
+        }
+      case 'start-pipeless-text':
+        if (!inComment) return true;
+        if (inPipelessText) {
+          unexpectedToken(
+            'start-pipeless-text', 'already in pipeless text mode',
+            filename, tok.line
+          );
+        }
+        inPipelessText = true;
+        return false;
+      case 'end-pipeless-text':
+        if (!inComment) return true;
+        if (!inPipelessText) {
+          unexpectedToken(
+            'end-pipeless-text', 'not in pipeless text mode',
+            filename, tok.line
+          );
+        }
+        inPipelessText = false;
+        inComment = false;
+        return false;
+      // There might be a `text` right after `comment` but before
+      // `start-pipeless-text`. Treat it accordingly.
+      case 'text':
+        return !inComment;
+      default:
+        if (inPipelessText) return false;
+        inComment = false;
+        return true;
+    }
+  });
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/LICENSE b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/LICENSE
new file mode 100644
index 0000000..a1421ff
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Forbes Lindesay
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/README.md b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/README.md
new file mode 100644
index 0000000..61ecc5a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/README.md
@@ -0,0 +1,22 @@
+# jade-error
+
+Standard error objects for jade.  This module is intended for use by the lexer, parser, loader, linker, code-generator and any plugins.
+
+[![Build Status](https://img.shields.io/travis/jadejs/jade-error/master.svg)](https://travis-ci.org/jadejs/jade-error)
+[![Dependency Status](https://img.shields.io/gemnasium/jadejs/jade-error.svg)](https://gemnasium.com/jadejs/jade-error)
+[![NPM version](https://img.shields.io/npm/v/jade-error.svg)](https://www.npmjs.org/package/jade-error)
+
+## Installation
+
+    npm install jade-error
+
+## Usage
+
+```js
+var err = error('MY_CODE', 'My message', {line: 3, filename: 'myfile', src: 'foo\nbar\nbaz\nbash\nbing'});
+throw err;
+```
+
+## License
+
+  MIT
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/index.js
new file mode 100644
index 0000000..6d85037
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/index.js
@@ -0,0 +1,32 @@
+'use strict';
+
+module.exports = makeError;
+function makeError(code, message, options) {
+  var line = options.line;
+  var filename = options.filename;
+  var src = options.src;
+  var fullMessage;
+  if (src) {
+    var lines = src.split('\n');
+    var start = Math.max(line - 3, 0)
+    var end = Math.min(lines.length, line + 3);
+    // Error context
+    var context = lines.slice(start, end).map(function(text, i){
+      var curr = i + start + 1;
+      return (curr == line ? '  > ' : '    ')
+        + curr
+        + '| '
+        + text;
+    }).join('\n');
+    fullMessage = (filename || 'Jade') + ':' + line + '\n' + context + '\n\n' + message;
+  } else {
+    fullMessage = (filename || 'Jade') + ':' + line + '\n\n' + message;
+  }
+  var err = new Error(fullMessage);
+  err.code = 'JADE:' + code;
+  err.msg = message;
+  err.line = line;
+  err.filename = filename;
+  err.src = src;
+  return err;
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/package.json
new file mode 100644
index 0000000..d8e7a47
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/node_modules/jade-error/package.json
@@ -0,0 +1,52 @@
+{
+  "name": "jade-error",
+  "version": "1.0.0",
+  "description": "Standard error objects for jade",
+  "keywords": [],
+  "files": [
+    "index.js"
+  ],
+  "dependencies": {},
+  "devDependencies": {
+    "istanbul": "^0.3.17",
+    "testit": "^2.0.2"
+  },
+  "scripts": {
+    "test": "node test",
+    "coverage": "istanbul cover test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jadejs/jade-error.git"
+  },
+  "author": {
+    "name": "Forbes Lindesay"
+  },
+  "license": "MIT",
+  "gitHead": "20c1ec8ebbfcdd694604551c5fa31e9f35de196e",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-error/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-error",
+  "_id": "jade-error@1.0.0",
+  "_shasum": "ecb0a1df423f39098ebebee4defbc751aa06d320",
+  "_from": "jade-error@>=1.0.0 <2.0.0",
+  "_npmVersion": "2.7.1",
+  "_nodeVersion": "1.6.2",
+  "_npmUser": {
+    "name": "forbeslindesay",
+    "email": "forbes@lindesay.co.uk"
+  },
+  "maintainers": [
+    {
+      "name": "forbeslindesay",
+      "email": "forbes@lindesay.co.uk"
+    }
+  ],
+  "dist": {
+    "shasum": "ecb0a1df423f39098ebebee4defbc751aa06d320",
+    "tarball": "http://registry.npmjs.org/jade-error/-/jade-error-1.0.0.tgz"
+  },
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-error/-/jade-error-1.0.0.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/package.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/package.json
new file mode 100644
index 0000000..394cffc
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/package.json
@@ -0,0 +1,55 @@
+{
+  "name": "jade-strip-comments",
+  "version": "1.0.0",
+  "description": "Strip comments from a Jade token stream (from the lexer)",
+  "keywords": [
+    "jade"
+  ],
+  "dependencies": {
+    "jade-error": "^1.0.0"
+  },
+  "devDependencies": {
+    "istanbul": "^0.3.2",
+    "line-json": "^1.0.0",
+    "testit": "^2.0.2"
+  },
+  "scripts": {
+    "test": "node test && npm run coverage",
+    "coverage": "istanbul cover test"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jadejs/jade-strip-comments.git"
+  },
+  "author": {
+    "name": "Timothy Gu",
+    "email": "timothygu99@gmail.com"
+  },
+  "license": "MIT",
+  "gitHead": "db1795b29ef07f6bc25ae31b1d16e9abbc79817f",
+  "bugs": {
+    "url": "https://github.com/jadejs/jade-strip-comments/issues"
+  },
+  "homepage": "https://github.com/jadejs/jade-strip-comments#readme",
+  "_id": "jade-strip-comments@1.0.0",
+  "_shasum": "e6e91ced3a5e526de642a9f43c770a1d80321da6",
+  "_from": "jade-strip-comments@*",
+  "_npmVersion": "2.13.3",
+  "_nodeVersion": "3.0.0",
+  "_npmUser": {
+    "name": "timothygu",
+    "email": "timothygu99@gmail.com"
+  },
+  "dist": {
+    "shasum": "e6e91ced3a5e526de642a9f43c770a1d80321da6",
+    "tarball": "http://registry.npmjs.org/jade-strip-comments/-/jade-strip-comments-1.0.0.tgz"
+  },
+  "maintainers": [
+    {
+      "name": "timothygu",
+      "email": "timothygu99@gmail.com"
+    }
+  ],
+  "directories": {},
+  "_resolved": "https://registry.npmjs.org/jade-strip-comments/-/jade-strip-comments-1.0.0.tgz"
+}
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.both.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.both.expected.json
new file mode 100644
index 0000000..478862a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.both.expected.json
@@ -0,0 +1,25 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"indent","line":4,"val":3}
+{"type":"code","line":4,"val":"var s = 'this'","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"case","line":5,"val":"s"}
+{"type":"indent","line":6,"val":5}
+{"type":"newline","line":7}
+{"type":"when","line":7,"val":"'this'"}
+{"type":"indent","line":8,"val":7}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"It's this!"}
+{"type":"outdent","line":9}
+{"type":"when","line":9,"val":"'that'"}
+{"type":"indent","line":10,"val":7}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"It's that!"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.buffered.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.buffered.expected.json
new file mode 100644
index 0000000..161122d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.buffered.expected.json
@@ -0,0 +1,26 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"indent","line":4,"val":3}
+{"type":"code","line":4,"val":"var s = 'this'","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"case","line":5,"val":"s"}
+{"type":"indent","line":6,"val":5}
+{"type":"comment","line":6,"val":" Comment","buffer":false}
+{"type":"newline","line":7}
+{"type":"when","line":7,"val":"'this'"}
+{"type":"indent","line":8,"val":7}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"It's this!"}
+{"type":"outdent","line":9}
+{"type":"when","line":9,"val":"'that'"}
+{"type":"indent","line":10,"val":7}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"It's that!"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.input.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.input.json
new file mode 100644
index 0000000..161122d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.input.json
@@ -0,0 +1,26 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"indent","line":4,"val":3}
+{"type":"code","line":4,"val":"var s = 'this'","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"case","line":5,"val":"s"}
+{"type":"indent","line":6,"val":5}
+{"type":"comment","line":6,"val":" Comment","buffer":false}
+{"type":"newline","line":7}
+{"type":"when","line":7,"val":"'this'"}
+{"type":"indent","line":8,"val":7}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"It's this!"}
+{"type":"outdent","line":9}
+{"type":"when","line":9,"val":"'that'"}
+{"type":"indent","line":10,"val":7}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"It's that!"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.unbuffered.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.unbuffered.expected.json
new file mode 100644
index 0000000..478862a
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments-in-case.unbuffered.expected.json
@@ -0,0 +1,25 @@
+{"type":"doctype","line":1,"val":"html"}
+{"type":"newline","line":2}
+{"type":"tag","line":2,"val":"html","selfClosing":false}
+{"type":"indent","line":3,"val":2}
+{"type":"tag","line":3,"val":"body","selfClosing":false}
+{"type":"indent","line":4,"val":3}
+{"type":"code","line":4,"val":"var s = 'this'","escape":false,"buffer":false}
+{"type":"newline","line":5}
+{"type":"case","line":5,"val":"s"}
+{"type":"indent","line":6,"val":5}
+{"type":"newline","line":7}
+{"type":"when","line":7,"val":"'this'"}
+{"type":"indent","line":8,"val":7}
+{"type":"tag","line":8,"val":"p","selfClosing":false}
+{"type":"text","line":8,"val":"It's this!"}
+{"type":"outdent","line":9}
+{"type":"when","line":9,"val":"'that'"}
+{"type":"indent","line":10,"val":7}
+{"type":"tag","line":10,"val":"p","selfClosing":false}
+{"type":"text","line":10,"val":"It's that!"}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"outdent","line":10}
+{"type":"eos","line":10}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.both.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.both.expected.json
new file mode 100644
index 0000000..ec497b7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.both.expected.json
@@ -0,0 +1,29 @@
+{"type":"newline","line":2}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"text","line":5,"val":"one"}
+{"type":"newline","line":6}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"two"}
+{"type":"outdent","line":9}
+{"type":"newline","line":13}
+{"type":"newline","line":17}
+{"type":"newline","line":22}
+{"type":"newline","line":27}
+{"type":"newline","line":31}
+{"type":"newline","line":32}
+{"type":"text","line":32,"val":" "}
+{"type":"newline","line":33}
+{"type":"text","line":33,"val":"asfd"}
+{"type":"newline","line":35}
+{"type":"tag","line":35,"val":"p","selfClosing":false}
+{"type":"text","line":35,"val":"five"}
+{"type":"newline","line":37}
+{"type":"class","line":37,"val":"foo"}
+{"type":"text","line":37,"val":"// not a comment"}
+{"type":"newline","line":38}
+{"type":"eos","line":38}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.buffered.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.buffered.expected.json
new file mode 100644
index 0000000..d995fbd
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.buffered.expected.json
@@ -0,0 +1,37 @@
+{"type":"newline","line":2}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"text","line":5,"val":"one"}
+{"type":"newline","line":6}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"two"}
+{"type":"outdent","line":9}
+{"type":"newline","line":13}
+{"type":"newline","line":17}
+{"type":"newline","line":22}
+{"type":"newline","line":27}
+{"type":"comment","line":27,"val":"","buffer":false}
+{"type":"text","line":28,"val":""}
+{"type":"start-pipeless-text","line":28}
+{"type":"text","line":29,"val":"asfd"}
+{"type":"newline","line":30}
+{"type":"text","line":30,"val":""}
+{"type":"end-pipeless-text","line":30}
+{"type":"newline","line":31}
+{"type":"comment","line":31,"val":"","buffer":false}
+{"type":"newline","line":32}
+{"type":"text","line":32,"val":" "}
+{"type":"newline","line":33}
+{"type":"text","line":33,"val":"asfd"}
+{"type":"newline","line":35}
+{"type":"tag","line":35,"val":"p","selfClosing":false}
+{"type":"text","line":35,"val":"five"}
+{"type":"newline","line":37}
+{"type":"class","line":37,"val":"foo"}
+{"type":"text","line":37,"val":"// not a comment"}
+{"type":"newline","line":38}
+{"type":"eos","line":38}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.input.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.input.json
new file mode 100644
index 0000000..a8c0206
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.input.json
@@ -0,0 +1,76 @@
+{"type":"newline","line":2}
+{"type":"comment","line":2,"val":" foo","buffer":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"comment","line":4,"val":" bar","buffer":true}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"text","line":5,"val":"one"}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":" baz","buffer":true}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"two"}
+{"type":"outdent","line":9}
+{"type":"comment","line":9,"val":"","buffer":true}
+{"type":"start-pipeless-text","line":9}
+{"type":"text","line":10,"val":"ul"}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"  li foo"}
+{"type":"newline","line":12}
+{"type":"text","line":12,"val":""}
+{"type":"end-pipeless-text","line":12}
+{"type":"newline","line":13}
+{"type":"comment","line":13,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":13}
+{"type":"text","line":14,"val":"// inline follow"}
+{"type":"newline","line":15}
+{"type":"text","line":15,"val":"li three"}
+{"type":"newline","line":16}
+{"type":"text","line":16,"val":""}
+{"type":"end-pipeless-text","line":16}
+{"type":"newline","line":17}
+{"type":"comment","line":17,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":17}
+{"type":"text","line":18,"val":"// inline followed by tags"}
+{"type":"newline","line":19}
+{"type":"text","line":19,"val":"ul"}
+{"type":"newline","line":20}
+{"type":"text","line":20,"val":"  li four"}
+{"type":"newline","line":21}
+{"type":"text","line":21,"val":""}
+{"type":"end-pipeless-text","line":21}
+{"type":"newline","line":22}
+{"type":"comment","line":22,"val":"if IE lt 9","buffer":true}
+{"type":"start-pipeless-text","line":22}
+{"type":"text","line":23,"val":"// inline"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"script(src='/lame.js')"}
+{"type":"newline","line":25}
+{"type":"text","line":25,"val":"// end-inline"}
+{"type":"newline","line":26}
+{"type":"text","line":26,"val":""}
+{"type":"end-pipeless-text","line":26}
+{"type":"newline","line":27}
+{"type":"comment","line":27,"val":"","buffer":false}
+{"type":"text","line":28,"val":""}
+{"type":"start-pipeless-text","line":28}
+{"type":"text","line":29,"val":"asfd"}
+{"type":"newline","line":30}
+{"type":"text","line":30,"val":""}
+{"type":"end-pipeless-text","line":30}
+{"type":"newline","line":31}
+{"type":"comment","line":31,"val":"","buffer":false}
+{"type":"newline","line":32}
+{"type":"text","line":32,"val":" "}
+{"type":"newline","line":33}
+{"type":"text","line":33,"val":"asfd"}
+{"type":"newline","line":35}
+{"type":"tag","line":35,"val":"p","selfClosing":false}
+{"type":"text","line":35,"val":"five"}
+{"type":"newline","line":37}
+{"type":"class","line":37,"val":"foo"}
+{"type":"text","line":37,"val":"// not a comment"}
+{"type":"newline","line":38}
+{"type":"eos","line":38}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.both.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.both.expected.json
new file mode 100644
index 0000000..b4b617b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.both.expected.json
@@ -0,0 +1,3 @@
+{"type":"newline","line":4}
+{"type":"newline","line":6}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.buffered.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.buffered.expected.json
new file mode 100644
index 0000000..73610d1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.buffered.expected.json
@@ -0,0 +1,19 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"s/s."}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":""}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"comment","line":4,"val":" test/cases/comments.source.jade","buffer":false}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"test/cases/comments.source.jade"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"when"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"()"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.input.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.input.json
new file mode 100644
index 0000000..73610d1
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.input.json
@@ -0,0 +1,19 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":1}
+{"type":"text","line":2,"val":"s/s."}
+{"type":"newline","line":3}
+{"type":"text","line":3,"val":""}
+{"type":"end-pipeless-text","line":3}
+{"type":"newline","line":4}
+{"type":"comment","line":4,"val":" test/cases/comments.source.jade","buffer":false}
+{"type":"text","line":5,"val":""}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":6}
+{"type":"text","line":7,"val":"test/cases/comments.source.jade"}
+{"type":"newline","line":8}
+{"type":"text","line":8,"val":"when"}
+{"type":"newline","line":9}
+{"type":"text","line":9,"val":"()"}
+{"type":"end-pipeless-text","line":9}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.unbuffered.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.unbuffered.expected.json
new file mode 100644
index 0000000..b4b617b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.source.unbuffered.expected.json
@@ -0,0 +1,3 @@
+{"type":"newline","line":4}
+{"type":"newline","line":6}
+{"type":"eos","line":9}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.unbuffered.expected.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.unbuffered.expected.json
new file mode 100644
index 0000000..dc975fe
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/cases/comments.unbuffered.expected.json
@@ -0,0 +1,68 @@
+{"type":"newline","line":2}
+{"type":"comment","line":2,"val":" foo","buffer":true}
+{"type":"newline","line":3}
+{"type":"tag","line":3,"val":"ul","selfClosing":false}
+{"type":"indent","line":4,"val":2}
+{"type":"comment","line":4,"val":" bar","buffer":true}
+{"type":"newline","line":5}
+{"type":"tag","line":5,"val":"li","selfClosing":false}
+{"type":"text","line":5,"val":"one"}
+{"type":"newline","line":6}
+{"type":"comment","line":6,"val":" baz","buffer":true}
+{"type":"newline","line":7}
+{"type":"tag","line":7,"val":"li","selfClosing":false}
+{"type":"text","line":7,"val":"two"}
+{"type":"outdent","line":9}
+{"type":"comment","line":9,"val":"","buffer":true}
+{"type":"start-pipeless-text","line":9}
+{"type":"text","line":10,"val":"ul"}
+{"type":"newline","line":11}
+{"type":"text","line":11,"val":"  li foo"}
+{"type":"newline","line":12}
+{"type":"text","line":12,"val":""}
+{"type":"end-pipeless-text","line":12}
+{"type":"newline","line":13}
+{"type":"comment","line":13,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":13}
+{"type":"text","line":14,"val":"// inline follow"}
+{"type":"newline","line":15}
+{"type":"text","line":15,"val":"li three"}
+{"type":"newline","line":16}
+{"type":"text","line":16,"val":""}
+{"type":"end-pipeless-text","line":16}
+{"type":"newline","line":17}
+{"type":"comment","line":17,"val":" block","buffer":true}
+{"type":"start-pipeless-text","line":17}
+{"type":"text","line":18,"val":"// inline followed by tags"}
+{"type":"newline","line":19}
+{"type":"text","line":19,"val":"ul"}
+{"type":"newline","line":20}
+{"type":"text","line":20,"val":"  li four"}
+{"type":"newline","line":21}
+{"type":"text","line":21,"val":""}
+{"type":"end-pipeless-text","line":21}
+{"type":"newline","line":22}
+{"type":"comment","line":22,"val":"if IE lt 9","buffer":true}
+{"type":"start-pipeless-text","line":22}
+{"type":"text","line":23,"val":"// inline"}
+{"type":"newline","line":24}
+{"type":"text","line":24,"val":"script(src='/lame.js')"}
+{"type":"newline","line":25}
+{"type":"text","line":25,"val":"// end-inline"}
+{"type":"newline","line":26}
+{"type":"text","line":26,"val":""}
+{"type":"end-pipeless-text","line":26}
+{"type":"newline","line":27}
+{"type":"newline","line":31}
+{"type":"newline","line":32}
+{"type":"text","line":32,"val":" "}
+{"type":"newline","line":33}
+{"type":"text","line":33,"val":"asfd"}
+{"type":"newline","line":35}
+{"type":"tag","line":35,"val":"p","selfClosing":false}
+{"type":"text","line":35,"val":"five"}
+{"type":"newline","line":37}
+{"type":"class","line":37,"val":"foo"}
+{"type":"text","line":37,"val":"// not a comment"}
+{"type":"newline","line":38}
+{"type":"eos","line":38}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/comment-in-comment.err.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/comment-in-comment.err.json
new file mode 100644
index 0000000..d8299e0
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/comment-in-comment.err.json
@@ -0,0 +1,5 @@
+{
+  "msg": "`comment` encountered when already in a comment",
+  "code": "JADE:UNEXPECTED_TOKEN",
+  "line": 2
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/comment-in-comment.input.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/comment-in-comment.input.json
new file mode 100644
index 0000000..771e19d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/comment-in-comment.input.json
@@ -0,0 +1,3 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"comment","line":2,"val":"","buffer":false}
+{"type":"eos","line":3}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/end.err.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/end.err.json
new file mode 100644
index 0000000..bf08cb8
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/end.err.json
@@ -0,0 +1,5 @@
+{
+  "msg": "`end-pipeless-text` encountered when not in pipeless text mode",
+  "code": "JADE:UNEXPECTED_TOKEN",
+  "line": 4
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/end.input.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/end.input.json
new file mode 100644
index 0000000..c99176d
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/end.input.json
@@ -0,0 +1,4 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"end-pipeless-text","line":4}
+{"type":"newline","line":4}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/startstart.err.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/startstart.err.json
new file mode 100644
index 0000000..849fff7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/startstart.err.json
@@ -0,0 +1,5 @@
+{
+  "msg": "`start-pipeless-text` encountered when already in pipeless text mode",
+  "code": "JADE:UNEXPECTED_TOKEN",
+  "line": 3
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/startstart.input.json b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/startstart.input.json
new file mode 100644
index 0000000..6a97589
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/errors/startstart.input.json
@@ -0,0 +1,6 @@
+{"type":"comment","line":1,"val":"","buffer":false}
+{"type":"start-pipeless-text","line":2}
+{"type":"start-pipeless-text","line":3}
+{"type":"end-pipeless-text","line":4}
+{"type":"newline","line":4}
+{"type":"eos","line":5}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/index.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/index.js
new file mode 100644
index 0000000..04805e7
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/index.js
@@ -0,0 +1,59 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var testit = require('testit');
+var lineJSON = require('line-json');
+var strip = require('../');
+
+var dir = __dirname + '/cases/';
+fs.readdirSync(dir).forEach(function (testCase) {
+  if (/\.input\.json$/.test(testCase)) {
+    testit(testCase, function () {
+      var stem = testCase.replace(/\.input\.json$/, '.');
+
+      function test (name, options) {
+        var input = fs.readFileSync(dir + testCase, 'utf8');
+        input = lineJSON.parse(input);
+        var expected = fs.readFileSync(dir + stem + name + '.expected.json', 'utf8');
+        expected = lineJSON.parse(expected);
+
+        var result = strip(input, options);
+        fs.writeFileSync(dir + stem + name + '.actual.json', lineJSON.stringify(result));
+        assert.deepEqual(expected, result);
+      }
+
+      test('unbuffered');
+      test('buffered',   { stripBuffered: true, stripUnbuffered: false });
+      test('both',       { stripBuffered: true });
+    });
+  }
+});
+
+var edir = __dirname + '/errors/';
+fs.readdirSync(edir).forEach(function (testCase) {
+  if (/\.input\.json$/.test(testCase)) {
+    testit(testCase, function () {
+      var stem = testCase.replace(/\.input\.json$/, '.');
+
+      var input = fs.readFileSync(edir + testCase, 'utf8');
+      input = lineJSON.parse(input);
+      var expected = fs.readFileSync(edir + stem + 'err.json', 'utf8');
+      expected = JSON.parse(expected);
+
+      var actual;
+      try {
+        strip(input);
+        throw new Error('Expected ' + testCase + ' to throw an exception.');
+      } catch (ex) {
+        if (!ex || !ex.code || !ex.code.indexOf('JADE:') === 0) throw ex;
+        actual = {
+          msg: ex.msg,
+          code: ex.code,
+          line: ex.line
+        };
+      }
+      assert.deepEqual(expected, actual);
+    });
+  }
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/update-test-cases.js b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/update-test-cases.js
new file mode 100644
index 0000000..3bbb09f
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/node_modules/jade-strip-comments/test/update-test-cases.js
@@ -0,0 +1,79 @@
+'use strict';
+
+var fs = require('fs');
+var assert = require('assert');
+var testit = require('testit');
+var lineJSON = require('line-json');
+var strip = require('../');
+
+var dir = __dirname + '/cases/';
+fs.readdirSync(dir).forEach(function (testCase) {
+  if (/\.input\.json$/.test(testCase)) {
+    testit(testCase, function () {
+      var stem = testCase.replace(/\.input\.json$/, '.');
+
+      function test (name, options) {
+        var expected;
+        var input = fs.readFileSync(dir + testCase, 'utf8');
+        input = lineJSON.parse(input);
+        try {
+          expected = fs.readFileSync(dir + stem + name + '.expected.json', 'utf8');
+          expected = lineJSON.parse(expected);
+        } catch (ex) {
+          if (ex.code !== 'ENOENT') throw ex;
+        }
+
+        var result = strip(input, options);
+        try {
+          assert.deepEqual(expected, result);
+        } catch (ex) {
+          console.log('Updating', testCase);
+          fs.writeFileSync(dir + stem + name + '.expected.json'. lineJSON.stringify(result));
+        }
+      }
+
+      test('unbuffered');
+      test('buffered',   { stripBuffered: true, stripUnbuffered: false });
+      test('both',       { stripBuffered: true });
+    });
+  }
+});
+
+var edir = __dirname + '/errors/';
+fs.readdirSync(edir).forEach(function (testCase) {
+  if (/\.input\.json$/.test(testCase)) {
+    testit(testCase, function () {
+      var stem = testCase.replace(/\.input\.json$/, '.');
+
+      var input = fs.readFileSync(edir + testCase, 'utf8');
+      input = lineJSON.parse(input);
+      var expected;
+      try {
+        expected = fs.readFileSync(edir + stem + 'err.json', 'utf8');
+        expected = JSON.parse(expected);
+      } catch (ex) {
+        if (ex.code !== 'ENOENT') throw ex;
+      }
+
+      var actual;
+      try {
+        strip(input);
+        throw new Error('Expected ' + testCase + ' to throw an exception.');
+      } catch (ex) {
+        if (!ex || !ex.code || !ex.code.indexOf('JADE:') === 0) throw ex;
+        actual = {
+          msg: ex.msg,
+          code: ex.code,
+          line: ex.line
+        };
+      }
+      try {
+        assert.deepEqual(expected, actual);
+      } catch (ex) {
+        console.log('Updating', testCase);
+        fs.writeFileSync(edir + stem + 'err.json',
+                         JSON.stringify(actual, null, '  '));
+      }
+    });
+  }
+});
diff --git a/Plugins/UnrealJS/Content/Scripts/path.js b/Plugins/UnrealJS/Content/Scripts/path.js
new file mode 100644
index 0000000..17747a6
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/path.js
@@ -0,0 +1,2 @@
+module.exports = {     
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/polyfill/timers.js b/Plugins/UnrealJS/Content/Scripts/polyfill/timers.js
new file mode 100644
index 0000000..c094c17
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/polyfill/timers.js
@@ -0,0 +1,38 @@
+(function (target) {
+    var makeWindowTimer = require('windowTimers')
+    if (Root == undefined || Root.OnTick == undefined) return
+    
+    var timerLoop = makeWindowTimer(target);
+
+    var current_time = 0
+    target.$time = 0
+
+    var nextTicks = []
+    
+    var elapsedTime = 0
+    function invoke_next(fn) {
+        fn(elapsedTime)
+    }
+
+    var root = function (_elapsedTime) {
+        elapsedTime = _elapsedTime
+
+        var prev = nextTicks
+        nextTicks = []
+        prev.forEach(invoke_next)        
+
+        current_time += elapsedTime
+        target.$time = current_time
+
+        // non blocking
+        timerLoop(true);
+    }
+
+    target.process = {
+        nextTick: function (fn) {
+            nextTicks.push(fn)
+        }
+    }
+
+    Root.OnTick.Add(root)    
+})(this)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/polyfill/unrealengine.js b/Plugins/UnrealJS/Content/Scripts/polyfill/unrealengine.js
new file mode 100644
index 0000000..6a66f2b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/polyfill/unrealengine.js
@@ -0,0 +1,3 @@
+(function (target) {
+    var lib = JavascriptLibrary.prototype    
+})(this)
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/polyfill/windowTimers.js b/Plugins/UnrealJS/Content/Scripts/polyfill/windowTimers.js
new file mode 100644
index 0000000..1fe61ad
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/polyfill/windowTimers.js
@@ -0,0 +1,262 @@
+/* Implementation of HTML Timers (setInterval/setTimeout) based on sleep.
+ *
+ * This file is provided under the following terms (MIT License):
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Copyright 2012 Kevin Locke <kevin@kevinlocke.name>
+ */
+/*jslint bitwise: true, evil: true */
+
+/**
+ * Adds methods to implement the HTML5 WindowTimers interface on a given
+ * object.
+ *
+ * Adds the following methods:
+ * <ul>
+ * <li>clearInterval</li>
+ * <li>clearTimeout</li>
+ * <li>setInterval</li>
+ * <li>setTimeout</li>
+ * </ul>
+ *
+ * See http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
+ * for the complete specification of these methods.
+ *
+ * Example Usage
+ * Browser compatibility in Rhino:
+ * <pre><code>// Note:  "this" refers to the global object in this example
+ * var timerLoop = makeWindowTimer(this, java.lang.Thread.sleep);
+ *
+ * // Run code which may add intervals/timeouts
+ *
+ * timerLoop();
+ * </code></pre>
+ *
+ * Browser compatibility in SpiderMonkey (smjs):
+ * <pre><code>// Note:  "this" refers to the global object in this example
+ * var timerLoop = makeWindowTimer(this, function (ms) { sleep(ms / 1000); });
+ *
+ * // Run code which may add intervals/timeouts
+ *
+ * timerLoop();
+ * </code></pre>
+ *
+ * For more esoteric uses, timerLoop will return instead of sleeping if passed
+ * <code>true</code> which will run only events which are pending at the moment
+ * timerLoop is called:
+ * <pre><code>// Note:  "this" refers to the global object in this example
+ * var timerLoop = makeWindowTimer(this, java.lang.Thread.sleep);
+ *
+ * // Run code which may add intervals/timeouts
+ *
+ * while (timerLoop(true)) {
+ *     print("Still waiting...");
+ *     // Do other work here, possibly adding more intervals/timeouts
+ * }
+ * </code></pre>
+ *
+ * @param {Object} target Object to which the methods should be added.
+ * @param {Function} sleep A function which sleeps for a specified number of
+ *     milliseconds.
+ * @return {Function} The function which runs the scheduled timers.
+ */
+function makeWindowTimer(target, sleep) {
+    "use strict";
+
+    var counter = 1,
+        inCallback = false,
+        // Map handle -> timer
+        timersByHandle = {},
+        // Min-heap of timers by time then handle, index 0 unused
+        timersByTime = [ null ];
+
+    /** Compares timers based on scheduled time and handle. */
+    function timerCompare(t1, t2) {
+        // Note:  Only need less-than for our uses
+        return t1.time < t2.time ? -1 :
+                (t1.time === t2.time && t1.handle < t2.handle ? -1 : 0);
+    }
+
+    /** Fix the heap invariant which may be violated at a given index */
+    function heapFixDown(heap, i, lesscmp) {
+        var j, tmp;
+
+        j = i * 2;
+        while (j < heap.length) {
+            if (j + 1 < heap.length &&
+                    lesscmp(heap[j + 1], heap[j]) < 0) {
+                j = j + 1;
+            }
+
+            if (lesscmp(heap[i], heap[j]) < 0) {
+                break;
+            }
+
+            tmp = heap[j];
+            heap[j] = heap[i];
+            heap[i] = tmp;
+            i = j;
+            j = i * 2;
+        }
+    }
+
+    /** Fix the heap invariant which may be violated at a given index */
+    function heapFixUp(heap, i, lesscmp) {
+        var j, tmp;
+        while (i > 1) {
+            j = i >> 1;     // Integer div by 2
+
+            if (lesscmp(heap[j], heap[i]) < 0) {
+                break;
+            }
+
+            tmp = heap[j];
+            heap[j] = heap[i];
+            heap[i] = tmp;
+            i = j;
+        }
+    }
+
+    /** Remove the minimum element from the heap */
+    function heapPop(heap, lesscmp) {
+        heap[1] = heap[heap.length - 1];
+        heap.pop();
+        heapFixDown(heap, 1, lesscmp);
+    }
+
+    /** Create a timer and schedule code to run at a given time */
+    function addTimer(code, delay, repeat, argsIfFn) {
+        var handle, timer;
+
+        if (typeof code !== "function") {
+            code = String(code);
+            argsIfFn = null;
+        }
+        delay = Number(delay) || 0;
+        if (inCallback) {
+            delay = Math.max(delay, 4);
+        }
+        // Note:  Must set handle after argument conversion to properly
+        // handle conformance test in HTML5 spec.
+        handle = counter;
+        counter += 1;
+
+        timer = {
+            args: argsIfFn,
+            cancel: false,
+            code: code,
+            handle: handle,
+            repeat: repeat ? Math.max(delay, 4) : 0,
+            time: $time * 1000 + delay
+        };
+
+        timersByHandle[handle] = timer;
+        timersByTime.push(timer);
+        heapFixUp(timersByTime, timersByTime.length - 1, timerCompare);
+
+        return handle;
+    }
+
+    /** Cancel an existing timer */
+    function cancelTimer(handle, repeat) {
+        var timer;
+
+        if (timersByHandle.hasOwnProperty(handle)) {
+            timer = timersByHandle[handle];
+            if (repeat === (timer.repeat > 0)) {
+                timer.cancel = true;
+            }
+        }
+    }
+
+    function clearInterval(handle) {
+        cancelTimer(handle, true);
+    }
+    target.clearInterval = clearInterval;
+
+    function clearTimeout(handle) {
+        cancelTimer(handle, false);
+    }
+    target.clearTimeout = clearTimeout;
+
+    function setInterval(code, delay) {
+        return addTimer(
+            code,
+            delay,
+            true,
+            Array.prototype.slice.call(arguments, 2)
+        );
+    }
+    target.setInterval = setInterval;
+
+    function setTimeout(code, delay) {
+        return addTimer(
+            code,
+            delay,
+            false,
+            Array.prototype.slice.call(arguments, 2)
+        );
+    }
+    target.setTimeout = setTimeout;
+
+    return function timerLoop(nonblocking) {
+        var now, timer;
+
+        // Note: index 0 unused in timersByTime
+        while (timersByTime.length > 1) {
+            timer = timersByTime[1];
+
+            if (timer.cancel) {
+                delete timersByHandle[timer.handle];
+                heapPop(timersByTime, timerCompare);
+            } else {
+                now = $time * 1000;
+                if (timer.time <= now) {
+                    inCallback = true;
+                    try {
+                        if (typeof timer.code === "function") {
+                            timer.code.apply(undefined, timer.args);
+                        } else {
+                            eval(timer.code);
+                        }
+                    } finally {
+                        inCallback = false;
+                    }
+
+                    if (timer.repeat > 0 && !timer.cancel) {
+                        timer.time += timer.repeat;
+                        heapFixDown(timersByTime, 1, timerCompare);
+                    } else {
+                        delete timersByHandle[timer.handle];
+                        heapPop(timersByTime, timerCompare);
+                    }
+                } else if (!nonblocking) {
+                    sleep(timer.time - now);
+                } else {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    };
+}
+
+module.exports = makeWindowTimer;
+// vi: set sts=4 sw=4 et :
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/request.js b/Plugins/UnrealJS/Content/Scripts/request.js
new file mode 100644
index 0000000..726f560
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/request.js
@@ -0,0 +1,75 @@
+(function () {
+	"use strict"
+	
+	function $get(url,succ,fail) {
+		var req = new JavascriptHttpRequest()
+		req.SetVerb("GET")
+		req.SetURL(url)
+		req.OnComplete.Add(function(success){
+			if (success) {
+				try {
+					var json = JSON.parse(req.GetContentAsString())
+					succ(json)
+					return    
+				}
+				catch (e) {                        
+				}          
+			}
+			if (fail) {
+				fail(req.GetResponseCode())
+			}
+		})
+		req.ProcessRequest()
+	}
+	
+	let _ = require('lodash')
+	let request = (verb,url,options = {}) => {
+		let headers = options.headers || {}			
+		let data = options.data	
+		let res = options.res || "json"		 			 
+		
+		return new Promise((resolve,reject) => {
+			let req = new JavascriptHttpRequest()
+			req.SetVerb(verb)
+			req.SetURL(url)
+			_.each(headers,(v,k) => {
+				req.SetHeader(k,v)
+			})
+			if (data) {
+				if (typeof data == 'string') { 						
+					req.SetContentAsString(data)
+				} else if (data instanceof ArrayBuffer) {
+					memory.bind(data)
+					req.SetContentFromMemory()
+					memory.unbind()
+				} else if (data) {
+					req.SetHeader('Content-Type','application/json')
+					req.SetContentAsString(JSON.stringify(data))
+				}
+			}
+			req.OnComplete = (successful) => {
+				if (successful) {
+					if (res == "json") {
+						try {
+							resolve(JSON.parse(req.GetContentAsString()))
+						}
+						catch (e) {
+							reject(new Error("Invalid JSON"))
+						}
+					} else if (res == "string") {
+						resolve(req.GetContentAsString())
+					} else if (res == "raw") {
+						resolve(req)
+					}
+				} else { 
+					reject(new Error(`${req.GetStatus()}`))
+				}			
+			}
+			req.ProcessRequest()				
+		})
+	}
+	
+	request.$get = $get // old deprecated
+	
+	module.exports = request
+}())
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/style.js b/Plugins/UnrealJS/Content/Scripts/style.js
new file mode 100644
index 0000000..883959b
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/style.js
@@ -0,0 +1,21 @@
+(function () {
+    'use strict'
+    var _ = require('lodash');
+	var split = require('css-split');
+    var instantiator = require('instantiator')
+	var conv = instantiator.conv;
+    
+	function Style(id,attrs) {
+		var s = split(id)
+        var doc = {
+			class : s.class,
+			type : s.type,
+            attrs: _.clone(conv(attrs)) || {id:s.id},
+            children: Array.prototype.slice.call(arguments,2)
+        };	
+        
+        return doc
+    }
+    
+    module.exports = Style;
+}())
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Content/Scripts/uclass.js b/Plugins/UnrealJS/Content/Scripts/uclass.js
new file mode 100644
index 0000000..c0ab8de
--- /dev/null
+++ b/Plugins/UnrealJS/Content/Scripts/uclass.js
@@ -0,0 +1,104 @@
+(function (global) {
+	"use strict";
+	
+	let _ = require('lodash')
+    let inputBinding = require('input-binding')
+	
+	module.exports = function () {
+		global.nextUnrealEngineClassId = global.nextUnrealEngineClassId || 0
+		function fetchClassId() {
+			return global.nextUnrealEngineClassId++
+		}
+
+		let mod_patterns = {
+		    bCtrl: /^ctrl$/i,
+		    bAlt: /^alt$/i,
+		    bCmd: /^cmd$/i,
+            bShift: /^shift$/i
+		}
+
+		let inputbinding_patterns = {
+		    Action: (a) => ({ InputActionName: a[1], InputKeyEvent: a[2] || 'IE_Pressed' }),
+		    Axis: (a) => ({ InputAxisName: a[1] }),
+		    AxisKey: (a) => ({ InputAxisKey: a[1] }),
+		    Key: (a) => ({ InputChord: ParseInputChord(a[1]), InputKeyEvent: a[2] || 'IE_Pressed' }),
+		    Touch: (a) => ({ InputKeyEvent: a[1] || 'IE_Pressed' }),
+		    VectorAxis: (a) => ({})
+		}
+
+		function ParseInputChord(string) {
+		    let a = (string || '').split('+')
+		    let key = a.pop()
+
+		    let out = {
+                Key : { KeyName : key }
+		    }
+
+		    a.forEach((mod) => {
+		        _.each(mod_patterns,(v,k) => {
+		            if (v.test(mod)) {
+		                out[k] = true
+		            }
+		        })		        
+		    })
+
+            return out
+		}
+		
+		function register(target, template) {
+		    let bindings = []
+
+			let splits = String(template).split(/[\s+|\r|\n]/);
+			let orgClassName = splits[1]
+			let className = `${orgClassName}_C${fetchClassId()}`
+			let parentClassName = splits[3]
+			let parentClass = eval(parentClassName) 
+			 
+			let proxy = {}
+			_.each(Object.getOwnPropertyNames(template.prototype), (k) => {
+			    proxy[k] = template.prototype[k]
+
+			    let s = String(proxy[k])
+			    let functionName = s.substr(0, s.indexOf('(')).replace(/\b/g, '')                
+			    s = s.substr(s.indexOf(')')+1)
+			    s = s.substr(0, s.indexOf('{'))
+			    s = s.substr(s.indexOf('/*') + 2)
+			    s = s.substr(0, s.indexOf('*/'))
+			    let a = s.split(/[\[\],]/)
+			    let flags = _.filter(a, (a) => /^[\-\+]/.test(a))
+			    a = _.filter(a,(a) => !/^[\-\+]/.test(a))
+			    if (/Binding$/i.test(a[0])) {
+			        let prefix = a[0].substr(0, a[0].length - 7)			        
+			        let pattern = inputbinding_patterns[prefix]
+			        if (!pattern) throw "Invalid binding pattern"
+
+			        let binding = {
+			            type: prefix, 
+			            FunctionNameToBind: functionName
+			        }
+			        flags.forEach((flag) => {
+                        binding[flag.substr(1)] = (flag[0] == '+')
+			        })
+			        bindings.push(_.extend(binding, pattern(a)))			        
+			    }
+			})
+			
+			let thePackage = JavascriptLibrary.CreatePackage(null,'/Script/Javascript')
+			
+			let klass = CreateClass(className,{
+				Parent:parentClass,
+				Functions:proxy,
+				Outer:thePackage
+			});
+			
+			if (target != undefined) {
+				target[orgClassName] = klass;
+			}
+
+            bindings.forEach((binding) => inputBinding(klass,binding))
+			return klass;
+		}		  
+		
+		return register;
+	}
+}(this))
diff --git a/Plugins/UnrealJS/LICENSE b/Plugins/UnrealJS/LICENSE
new file mode 100644
index 0000000..cd123dc
--- /dev/null
+++ b/Plugins/UnrealJS/LICENSE
@@ -0,0 +1,190 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   Copyright 2015 NCSOFT Corporation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Resources/Icon128.png b/Plugins/UnrealJS/Resources/Icon128.png
new file mode 100644
index 0000000..424f9f7
Binary files /dev/null and b/Plugins/UnrealJS/Resources/Icon128.png differ
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/JavascriptConsole.Build.cs b/Plugins/UnrealJS/Source/JavascriptConsole/JavascriptConsole.Build.cs
new file mode 100644
index 0000000..428a1ec
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/JavascriptConsole.Build.cs
@@ -0,0 +1,23 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+using UnrealBuildTool;
+
+public class JavascriptConsole : ModuleRules
+{
+    public JavascriptConsole(TargetInfo Target)
+	{
+		PrivateDependencyModuleNames.AddRange(
+			new string[] {
+				"Core",
+				"CoreUObject", // @todo Mac: for some reason it's needed to link in debug on Mac
+				"Engine",
+                "InputCore",
+				"UnrealEd",
+				"Slate",
+				"SlateCore",
+                "EditorStyle",
+                "V8"
+			}
+		);
+	}
+}
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptConsoleModule.cpp b/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptConsoleModule.cpp
new file mode 100644
index 0000000..40b0a93
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptConsoleModule.cpp
@@ -0,0 +1,159 @@
+#include "JavascriptLogPrivatePCH.h"
+#include "SJavascriptConsole.h"
+#include "SJavascriptLog.h"
+#include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructureModule.h"
+#include "SDockTab.h"
+
+IMPLEMENT_MODULE( FJavascriptConsoleModule, JavascriptLog );
+
+namespace JavascriptConsoleModule
+{
+	static const FName JavascriptLogTabName = FName(TEXT("JavascriptLog"));
+}
+
+/** This class is to capture all log output even if the log window is closed */
+class FJavascriptLogHistory : public FOutputDevice
+{
+public:
+
+	FJavascriptLogHistory()
+	{
+		GLog->AddOutputDevice(this);
+		GLog->SerializeBacklog(this);
+	}
+
+	~FJavascriptLogHistory()
+	{
+		// At shutdown, GLog may already be null
+		if( GLog != NULL )
+		{
+			GLog->RemoveOutputDevice(this);
+		}
+	}
+
+	/** Gets all captured messages */
+	const TArray< TSharedPtr<FLogMessage> >& GetMessages() const
+	{
+		return Messages;
+	}
+
+protected:
+
+	virtual void Serialize( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category ) override
+	{
+		// Capture all incoming messages and store them in history
+		SJavascriptLog::CreateLogMessages(V, Verbosity, Category, Messages);
+	}
+
+private:
+
+	/** All log messsges since this module has been started */
+	TArray< TSharedPtr<FLogMessage> > Messages;
+};
+
+/** Our global output log app spawner */
+static TSharedPtr<FJavascriptLogHistory> JavascriptLogHistory;
+
+TSharedRef<SDockTab> SpawnJavascriptLog( const FSpawnTabArgs& Args )
+{
+	return SNew(SDockTab)
+		.Icon(FEditorStyle::GetBrush("Log.TabIcon"))
+		.TabRole( ETabRole::NomadTab )
+		.Label( NSLOCTEXT("JavascriptConsole", "TabTitle", "Javascript Console") )
+		[
+			SNew(SJavascriptLog).Messages( JavascriptLogHistory->GetMessages() )
+		];
+}
+
+void FJavascriptConsoleModule::StartupModule()
+{
+	FGlobalTabmanager::Get()->RegisterNomadTabSpawner(JavascriptConsoleModule::JavascriptLogTabName, FOnSpawnTab::CreateStatic( &SpawnJavascriptLog ) )
+		.SetDisplayName(NSLOCTEXT("UnrealEditor", "JavascriptLogTab", "Javascript Console"))
+		.SetTooltipText(NSLOCTEXT("UnrealEditor", "JavascriptLogTooltipText", "Open the Javascript Console tab."))
+		.SetGroup( WorkspaceMenu::GetMenuStructure().GetDeveloperToolsLogCategory() )
+		.SetIcon( FSlateIcon(FEditorStyle::GetStyleSetName(), "Log.TabIcon") );
+	
+	JavascriptLogHistory = MakeShareable(new FJavascriptLogHistory);
+}
+
+void FJavascriptConsoleModule::ShutdownModule()
+{
+	if (FSlateApplication::IsInitialized())
+	{
+		FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(JavascriptConsoleModule::JavascriptLogTabName);
+	}
+}
+
+TSharedRef< SWidget > FJavascriptConsoleModule::MakeConsoleInputBox( TSharedPtr< SEditableTextBox >& OutExposedEditableTextBox ) const
+{
+	TSharedRef< SJavascriptConsoleInputBox > NewConsoleInputBox = SNew( SJavascriptConsoleInputBox );
+	OutExposedEditableTextBox = NewConsoleInputBox->GetEditableTextBox();
+	return NewConsoleInputBox;
+}
+
+
+void FJavascriptConsoleModule::ToggleJavascriptConsoleForWindow( const TSharedRef< SWindow >& Window, const EJavascriptConsoleStyle::Type InStyle, const FJavascriptConsoleDelegates& JavascriptConsoleDelegates )
+{
+	bool bShouldOpen = true;
+	// Close an existing console box, if there is one
+	TSharedPtr< SWidget > PinnedJavascriptConsole( JavascriptConsole.Pin() );
+	if( PinnedJavascriptConsole.IsValid() )
+	{
+		// If the console is already open close it unless it is in a different window.  In that case reopen it on that window
+		bShouldOpen = false;
+		TSharedPtr< SWindow > WindowForExistingConsole = FSlateApplication::Get().FindWidgetWindow(PinnedJavascriptConsole.ToSharedRef());
+		if (WindowForExistingConsole.IsValid())
+		{
+			WindowForExistingConsole->RemoveOverlaySlot(PinnedJavascriptConsole.ToSharedRef());
+			JavascriptConsole.Reset();
+		}
+
+		if( WindowForExistingConsole != Window )
+		{
+			// Console is being opened on another window
+			bShouldOpen = true;
+		}
+	}
+	
+	TSharedPtr<SDockTab> ActiveTab = FGlobalTabmanager::Get()->GetActiveTab();
+	if (ActiveTab.IsValid() && ActiveTab->GetLayoutIdentifier() == FTabId(JavascriptConsoleModule::JavascriptLogTabName))
+	{
+		FGlobalTabmanager::Get()->DrawAttention(ActiveTab.ToSharedRef());
+		bShouldOpen = false;
+	}
+
+	if( bShouldOpen )
+	{
+		const EJavascriptConsoleStyle::Type JavascriptConsoleStyle = InStyle;
+		TSharedRef< SJavascriptConsole > JavascriptConsoleRef = SNew( SJavascriptConsole, JavascriptConsoleStyle, this, &JavascriptConsoleDelegates );
+		JavascriptConsole = JavascriptConsoleRef;
+
+		const int32 MaximumZOrder = MAX_int32;
+		Window->AddOverlaySlot( MaximumZOrder )
+		.VAlign(VAlign_Bottom)
+		.HAlign(HAlign_Center)
+		.Padding( 10.0f )
+		[
+			JavascriptConsoleRef
+		];
+
+		// Force keyboard focus
+		JavascriptConsoleRef->SetFocusToEditableText();
+	}
+}
+
+
+void FJavascriptConsoleModule::CloseJavascriptConsole()
+{
+	TSharedPtr< SWidget > PinnedJavascriptConsole( JavascriptConsole.Pin() );
+
+	if( PinnedJavascriptConsole.IsValid() )
+	{
+		TSharedPtr< SWindow > WindowForExistingConsole = FSlateApplication::Get().FindWidgetWindow(PinnedJavascriptConsole.ToSharedRef());
+		if (WindowForExistingConsole.IsValid())
+		{
+			WindowForExistingConsole->RemoveOverlaySlot( PinnedJavascriptConsole.ToSharedRef() );
+			JavascriptConsole.Reset();
+		}
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptConsolePrivatePCH.h b/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptConsolePrivatePCH.h
new file mode 100644
index 0000000..cf54926
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptConsolePrivatePCH.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "UnrealEd.h"
+#include "JavascriptConsoleModule.h"
+#include "SlateBasics.h"
+
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptLogPrivatePCH.h b/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptLogPrivatePCH.h
new file mode 100644
index 0000000..cf54926
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/JavascriptLogPrivatePCH.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "UnrealEd.h"
+#include "JavascriptConsoleModule.h"
+#include "SlateBasics.h"
+
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptConsole.cpp b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptConsole.cpp
new file mode 100644
index 0000000..0fe605f
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptConsole.cpp
@@ -0,0 +1,135 @@
+#include "JavascriptLogPrivatePCH.h"
+#include "JavascriptConsoleModule.h"
+#include "SJavascriptConsole.h"
+#include "SJavascriptLog.h"
+
+namespace JavascriptConsoleDefs
+{
+	// How many seconds to animate when console is summoned
+	static const float IntroAnimationDuration = 0.25f;
+}
+
+BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
+
+void SJavascriptConsole::Construct( const FArguments& InArgs, const EJavascriptConsoleStyle::Type InStyle, FJavascriptConsoleModule* JavascriptConsoleModule, const FJavascriptConsoleDelegates* JavascriptConsoleDelegates )
+{
+	CurrentStyle = InStyle;
+
+	TSharedPtr<SJavascriptConsoleInputBox> ConsoleInputBox;
+	
+	check( JavascriptConsoleModule != NULL );
+	ChildSlot
+	[ 
+		SNew( SVerticalBox )
+
+		+SVerticalBox::Slot()
+		.AutoHeight()
+		[
+			SNew( SVerticalBox )
+				.Visibility( this, &SJavascriptConsole::MakeVisibleIfLogIsShown )
+					
+				+SVerticalBox::Slot()
+				.AutoHeight()
+				.Padding( 10.0f )
+				[
+					SNew(SBox)
+					.HeightOverride( 200.0f )
+					[
+						SNew( SBorder )
+							.BorderImage( FEditorStyle::GetBrush( "ToolPanel.GroupBorder" ) )
+							.ColorAndOpacity( this, &SJavascriptConsole::GetAnimatedColorAndOpacity )
+							.BorderBackgroundColor( this, &SJavascriptConsole::GetAnimatedSlateColor )
+							[
+								SNew( SSpacer )
+							]
+					]
+				]
+		]
+
+		+SVerticalBox::Slot()
+		.AutoHeight()
+		.Padding( 10.0f )
+		[
+			SNew(SBox)
+			.HeightOverride( 26.0f )
+			.HAlign( HAlign_Left )
+			[
+				SNew( SBorder )
+				.Padding( FMargin(2) )
+				.BorderImage( FEditorStyle::GetBrush( "JavascriptConsole.Background" ) )
+				.ColorAndOpacity( this, &SJavascriptConsole::GetAnimatedColorAndOpacity )
+				.BorderBackgroundColor( this, &SJavascriptConsole::GetAnimatedSlateColor )
+				[
+					SNew(SHorizontalBox)
+					+ SHorizontalBox::Slot()
+					.AutoWidth()
+					.Padding(3.0f)
+					.VAlign(VAlign_Center)
+					[
+						SNew(STextBlock)
+						.Text(NSLOCTEXT("Console", "ConsoleLabel", "Console"))
+
+					]
+					+ SHorizontalBox::Slot()
+					.Padding(5.0f, 2.0f)
+					.VAlign(VAlign_Center)
+					.MaxWidth(400.0f)
+					[
+						SAssignNew(ConsoleInputBox, SJavascriptConsoleInputBox)
+						.OnConsoleCommandExecuted(JavascriptConsoleDelegates->OnConsoleCommandExecuted)
+					]
+				]
+			]
+		]
+	];
+
+	EditableTextBox = ConsoleInputBox->GetEditableTextBox();
+
+	// Kick off intro animation
+	AnimCurveSequence = FCurveSequence();
+	AnimCurve = AnimCurveSequence.AddCurve( 0.0f, JavascriptConsoleDefs::IntroAnimationDuration, ECurveEaseFunction::QuadOut );
+	FlashCurve = AnimCurveSequence.AddCurve( JavascriptConsoleDefs::IntroAnimationDuration, .15f, ECurveEaseFunction::QuadInOut );
+
+	AnimCurveSequence.Play(this->AsShared());
+}
+END_SLATE_FUNCTION_BUILD_OPTIMIZATION
+
+SJavascriptConsole::SJavascriptConsole()
+	: CurrentStyle( EJavascriptConsoleStyle::Compact )
+{
+}
+
+
+void SJavascriptConsole::SetFocusToEditableText()
+{
+	FSlateApplication::Get().SetKeyboardFocus( EditableTextBox.ToSharedRef(), EFocusCause::SetDirectly );
+}
+
+EVisibility SJavascriptConsole::MakeVisibleIfLogIsShown() const
+{
+	return CurrentStyle == EJavascriptConsoleStyle::WithLog ? EVisibility::Visible : EVisibility::Collapsed;
+}
+
+
+FLinearColor SJavascriptConsole::GetAnimatedColorAndOpacity() const
+{
+	return FLinearColor( 1.0f, 1.0f, 1.0f, AnimCurve.GetLerp() );
+}
+
+
+FSlateColor SJavascriptConsole::GetAnimatedSlateColor() const
+{
+	return FSlateColor( GetAnimatedColorAndOpacity() );
+}
+
+FSlateColor SJavascriptConsole::GetFlashColor() const
+{
+	float FlashAlpha = 1.0f - FlashCurve.GetLerp();
+
+	if (FlashAlpha == 1.0f)
+	{
+		FlashAlpha = 0.0f;
+	}
+
+	return FLinearColor(1,1,1,FlashAlpha);
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptConsole.h b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptConsole.h
new file mode 100644
index 0000000..5dcb6d0
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptConsole.h
@@ -0,0 +1,51 @@
+#pragma once
+
+
+/**
+ * Debug console widget, designed to be summoned on top of a viewport or window
+ */
+class SJavascriptConsole : public SCompoundWidget
+{
+public:
+
+	SLATE_BEGIN_ARGS( SJavascriptConsole )	
+	{
+	}
+
+	SLATE_END_ARGS()
+
+	/** Constructs this widget */
+	void Construct( const FArguments& InArgs, const EJavascriptConsoleStyle::Type InStyle, FJavascriptConsoleModule* JavascriptConsoleModule, const FJavascriptConsoleDelegates* JavascriptConsoleDelegates );
+
+	/** Call to set focus to this debug console's editable text box */
+	void SetFocusToEditableText();
+
+	/** Default constructor */
+	SJavascriptConsole();
+
+
+protected:
+	/** Returns EVisibility::Visible if style has log being shown, otherwise VIS_COLLAPSED */
+	EVisibility MakeVisibleIfLogIsShown() const;
+
+	/** Returns a color and opacity value to use based on any current animation */
+	FLinearColor GetAnimatedColorAndOpacity() const;
+
+	/** Returns a Slate color based on any current animation (same color as GetAnimatedColorAndOpacity) */
+	FSlateColor GetAnimatedSlateColor() const;
+
+	FSlateColor GetFlashColor() const;
+private:
+
+	/** Editable text box for this debug console's input line */
+	TSharedPtr< SEditableTextBox > EditableTextBox;
+
+	/** Current style of the debug console.  Can be changed on the fly. */
+	EJavascriptConsoleStyle::Type CurrentStyle;
+
+	/** Intro/outro animation curve */
+	FCurveSequence AnimCurveSequence;
+	FCurveHandle AnimCurve;
+	FCurveHandle FlashCurve;
+};
+
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptLog.cpp b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptLog.cpp
new file mode 100644
index 0000000..1adcb1d
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptLog.cpp
@@ -0,0 +1,846 @@
+#include "JavascriptLogPrivatePCH.h"
+#include "SJavascriptLog.h"
+#include "SScrollBorder.h"
+#include "BaseTextLayoutMarshaller.h"
+#include "GameFramework/GameMode.h"
+#include "Engine/LocalPlayer.h"
+#include "GameFramework/GameState.h"
+#include "IV8.h"
+#include "STextComboBox.h"
+
+#define LOCTEXT_NAMESPACE "JavascriptConsole"
+
+/** Custom console editable text box whose only purpose is to prevent some keys from being typed */
+class SJavascriptConsoleEditableTextBox : public SEditableTextBox
+{
+public:
+	SLATE_BEGIN_ARGS( SJavascriptConsoleEditableTextBox ) {}
+		
+		/** Hint text that appears when there is no text in the text box */
+		SLATE_ATTRIBUTE(FText, HintText)
+
+		/** Called whenever the text is changed interactively by the user */
+		SLATE_EVENT(FOnTextChanged, OnTextChanged)
+
+		/** Called whenever the text is committed.  This happens when the user presses enter or the text box loses focus. */
+		SLATE_EVENT(FOnTextCommitted, OnTextCommitted)
+
+	SLATE_END_ARGS()
+
+
+	void Construct( const FArguments& InArgs )
+	{
+		SetStyle(&FCoreStyle::Get().GetWidgetStyle< FEditableTextBoxStyle >("NormalEditableTextBox"));
+
+		SBorder::Construct(SBorder::FArguments()
+			.BorderImage(this, &SJavascriptConsoleEditableTextBox::GetConsoleBorder)
+			.BorderBackgroundColor(Style->BackgroundColor)
+			.ForegroundColor(Style->ForegroundColor)
+			.Padding(Style->Padding)
+			[
+				SAssignNew( EditableText, SJavascriptConsoleEditableText )
+				.HintText( InArgs._HintText )
+				.OnTextChanged( InArgs._OnTextChanged )
+				.OnTextCommitted( InArgs._OnTextCommitted )
+			] );
+	}
+
+private:
+	class SJavascriptConsoleEditableText : public SEditableText
+	{
+	public:
+		SLATE_BEGIN_ARGS( SJavascriptConsoleEditableText ) {}
+			/** The text that appears when there is nothing typed into the search box */
+			SLATE_ATTRIBUTE(FText, HintText)
+			/** Called whenever the text is changed interactively by the user */
+			SLATE_EVENT(FOnTextChanged, OnTextChanged)
+
+			/** Called whenever the text is committed.  This happens when the user presses enter or the text box loses focus. */
+			SLATE_EVENT(FOnTextCommitted, OnTextCommitted)
+		SLATE_END_ARGS()
+
+		void Construct( const FArguments& InArgs )
+		{
+			SEditableText::Construct
+			( 
+				SEditableText::FArguments()
+				.HintText( InArgs._HintText )
+				.OnTextChanged( InArgs._OnTextChanged )
+				.OnTextCommitted( InArgs._OnTextCommitted ) 
+				.ClearKeyboardFocusOnCommit( false )
+				.IsCaretMovedWhenGainFocus( false ) 
+				.MinDesiredWidth( 400.0f )
+			);
+		}
+
+		virtual FReply OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent )
+		{
+			// Special case handling.  Intercept the tilde key.  It is not suitable for typing in the console
+			if( InKeyEvent.GetKey() == EKeys::Tilde )
+			{
+				return FReply::Unhandled();
+			}
+			else
+			{
+				return SEditableText::OnKeyDown( MyGeometry, InKeyEvent );
+			}
+		}
+
+		virtual FReply OnKeyChar( const FGeometry& MyGeometry, const FCharacterEvent& InCharacterEvent )
+		{
+			// Special case handling.  Intercept the tilde key.  It is not suitable for typing in the console
+			if( InCharacterEvent.GetCharacter() != 0x60 )
+			{
+				return SEditableText::OnKeyChar( MyGeometry, InCharacterEvent );
+			}
+			else
+			{
+				return FReply::Unhandled();
+			}
+		}
+
+	};
+
+	/** @return Border image for the text box based on the hovered and focused state */
+	const FSlateBrush* GetConsoleBorder() const
+	{
+		if (EditableText->HasKeyboardFocus())
+		{
+			return &Style->BackgroundImageFocused;
+		}
+		else
+		{
+			if (EditableText->IsHovered())
+			{
+				return &Style->BackgroundImageHovered;
+			}
+			else
+			{
+				return &Style->BackgroundImageNormal;
+			}
+		}
+	}
+
+};
+
+SJavascriptConsoleInputBox::SJavascriptConsoleInputBox()
+	: SelectedSuggestion(-1)
+	, bIgnoreUIUpdate(false)
+{
+}
+
+BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
+void SJavascriptConsoleInputBox::Construct( const FArguments& InArgs )
+{
+	OnConsoleCommandExecuted = InArgs._OnConsoleCommandExecuted;	
+
+	ContextArray.Add(MakeShareable(new FString(TEXT("Test"))));
+
+	ChildSlot
+	[
+		SNew(SHorizontalBox)		
+
+		+ SHorizontalBox::Slot()
+		.FillWidth(0.85f)
+		[		
+
+			SAssignNew( SuggestionBox, SMenuAnchor )
+				.Placement( InArgs._SuggestionListPlacement )
+				[
+					SAssignNew(InputText, SJavascriptConsoleEditableTextBox)
+						.OnTextCommitted(this, &SJavascriptConsoleInputBox::OnTextCommitted)
+						.HintText( NSLOCTEXT( "JavascriptConsole", "TypeInConsoleHint", "Enter javascript command" ) )
+						.OnTextChanged(this, &SJavascriptConsoleInputBox::OnTextChanged)
+						.IsEnabled_Lambda([this]{ return TargetContext.IsValid(); })
+				]
+				.MenuContent
+				(
+					SNew(SBorder)
+					.BorderImage(FEditorStyle::GetBrush("Menu.Background"))
+					.Padding( FMargin(2) )
+					[
+						SNew(SBox)
+						.HeightOverride(250) // avoids flickering, ideally this would be adaptive to the content without flickering
+						[
+							SAssignNew(SuggestionListView, SListView< TSharedPtr<FString> >)
+								.ListItemsSource(&Suggestions)
+								.SelectionMode( ESelectionMode::Single )							// Ideally the mouse over would not highlight while keyboard controls the UI
+								.OnGenerateRow(this, &SJavascriptConsoleInputBox::MakeSuggestionListItemWidget)
+								.OnSelectionChanged(this, &SJavascriptConsoleInputBox::SuggestionSelectionChanged)
+								.ItemHeight(18)
+						]
+					]
+				)
+		]
+
+		+ SHorizontalBox::Slot()
+		.FillWidth(0.15f)
+		[
+			// initial culture combo box
+			SAssignNew(ContextComboBox, STextComboBox)
+			.OptionsSource(&ContextArray)
+			.OnSelectionChanged_Lambda([this](TSharedPtr<FString> Selection, ESelectInfo::Type SelectInfo){
+				TargetContext = Selection;
+			})
+			.IsEnabled_Lambda([this]{ return ContextArray.Num() > 0; })
+			.ToolTipText(LOCTEXT("JavascriptContextId_Tooltip", "Javascript ContextId to interact with"))
+		]
+	];
+}
+void SJavascriptConsoleInputBox::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
+{
+	// Update selected item
+	{
+		auto OldArray = ContextArray;
+		ContextArray.Empty();
+		IV8::Get().GetContextIds(ContextArray);
+
+		for (auto Item : ContextArray)
+		{
+			if (!OldArray.Contains(Item))
+			{
+				ContextComboBox->SetSelectedItem(Item);
+			}
+		}
+
+		if (ContextArray.Num() && !ContextArray.Contains(TargetContext))
+		{
+			ContextComboBox->SetSelectedItem(ContextArray[0]);
+		}
+	}
+
+	if (!GIntraFrameDebuggingGameThread && !IsEnabled())
+	{
+		SetEnabled(true);
+	}
+	else if (GIntraFrameDebuggingGameThread && IsEnabled())
+	{
+		SetEnabled(false);
+	}
+}
+
+
+void SJavascriptConsoleInputBox::SuggestionSelectionChanged(TSharedPtr<FString> NewValue, ESelectInfo::Type SelectInfo)
+{
+	if(bIgnoreUIUpdate)
+	{
+		return;
+	}
+
+	for(int32 i = 0; i < Suggestions.Num(); ++i)
+	{
+		if(NewValue == Suggestions[i])
+		{
+			SelectedSuggestion = i;
+			MarkActiveSuggestion();
+
+			// If the user selected this suggestion by clicking on it, then go ahead and close the suggestion
+			// box as they've chosen the suggestion they're interested in.
+			if( SelectInfo == ESelectInfo::OnMouseClick )
+			{
+				SuggestionBox->SetIsOpen( false );
+			}
+
+			// Ideally this would set the focus back to the edit control
+//			FWidgetPath WidgetToFocusPath;
+//			FSlateApplication::Get().GeneratePathToWidgetUnchecked( InputText.ToSharedRef(), WidgetToFocusPath );
+//			FSlateApplication::Get().SetKeyboardFocus( WidgetToFocusPath, EFocusCause::SetDirectly );
+			break;
+		}
+	}
+}
+
+
+TSharedRef<ITableRow> SJavascriptConsoleInputBox::MakeSuggestionListItemWidget(TSharedPtr<FString> Text, const TSharedRef<STableViewBase>& OwnerTable)
+{
+	check(Text.IsValid());
+
+	FString Left, Right, Combined;
+
+	if(Text->Split(TEXT("\t"), &Left, &Right))
+	{
+		Combined = Left + Right;
+	}
+	else
+	{
+		Combined = *Text;
+	}
+
+	FText HighlightText = FText::FromString(Left);
+
+	return
+		SNew(STableRow< TSharedPtr<FString> >, OwnerTable)
+		[
+			SNew(SBox)
+			.WidthOverride(300)			// to enforce some minimum width, ideally we define the minimum, not a fixed width
+			[
+				SNew(STextBlock)
+				.Text(FText::FromString(Combined))
+				.TextStyle( FEditorStyle::Get(), "Log.Normal")
+				.HighlightText(HighlightText)
+			]
+		];
+}
+
+class FConsoleVariableAutoCompleteVisitor 
+{
+public:
+	// @param Name must not be 0
+	// @param CVar must not be 0
+	static void OnConsoleVariable(const TCHAR *Name, IConsoleObject* CVar,TArray<FString>& Sink)
+	{
+#if (UE_BUILD_SHIPPING || UE_BUILD_TEST)
+		if(CVar->TestFlags(ECVF_Cheat))
+		{
+			return;
+		}
+#endif // (UE_BUILD_SHIPPING || UE_BUILD_TEST)
+		if(CVar->TestFlags(ECVF_Unregistered))
+		{
+			return;
+		}
+
+		Sink.Add(Name);
+	}
+};
+
+void SJavascriptConsoleInputBox::OnTextChanged(const FText& InText)
+{
+	if(bIgnoreUIUpdate)
+	{
+		return;
+	}
+
+	const FString& InputTextStr = InputText->GetText().ToString();
+	if(!InputTextStr.IsEmpty())
+	{
+		TArray<FString> AutoCompleteList;
+
+		// javascript commandline
+		{
+			IV8::Get().FillAutoCompletion(TargetContext, AutoCompleteList, *InputTextStr);			
+		}
+
+		AutoCompleteList.Sort();
+
+		for(uint32 i = 0; i < (uint32)AutoCompleteList.Num(); ++i)
+		{
+			FString &ref = AutoCompleteList[i];
+
+			ref = ref.Left(InputTextStr.Len()) + TEXT("\t") + ref.RightChop(InputTextStr.Len());
+		}
+
+		SetSuggestions(AutoCompleteList, false);
+	}
+	else
+	{
+		ClearSuggestions();
+	}
+}
+
+void SJavascriptConsoleInputBox::OnTextCommitted( const FText& InText, ETextCommit::Type CommitInfo)
+{
+	if (CommitInfo == ETextCommit::OnEnter)
+	{
+		if (!InText.IsEmpty())
+		{
+			IConsoleManager::Get().AddConsoleHistoryEntry( *InText.ToString() );
+
+			// Copy the exec text string out so we can clear the widget's contents.  If the exec command spawns
+			// a new window it can cause the text box to lose focus, which will result in this function being
+			// re-entered.  We want to make sure the text string is empty on re-entry, so we'll clear it out
+			const FString ExecString = InText.ToString();
+
+			// Clear the console input area
+			bIgnoreUIUpdate = true;
+			InputText->SetText(FText::GetEmpty());
+			bIgnoreUIUpdate = false;
+			
+			// Exec!
+			{
+				IV8::Get().Exec(TargetContext, *ExecString);
+			}
+		}
+
+		ClearSuggestions();
+
+		OnConsoleCommandExecuted.ExecuteIfBound();
+	}
+}
+
+FReply SJavascriptConsoleInputBox::OnPreviewKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
+{
+	if(SuggestionBox->IsOpen())
+	{
+		if(KeyEvent.GetKey() == EKeys::Up || KeyEvent.GetKey() == EKeys::Down)
+		{
+			if(KeyEvent.GetKey() == EKeys::Up)
+			{
+				if(SelectedSuggestion < 0)
+				{
+					// from edit control to end of list
+					SelectedSuggestion = Suggestions.Num() - 1;
+				}
+				else
+				{
+					// got one up, possibly back to edit control
+					--SelectedSuggestion;
+				}
+			}
+
+			if(KeyEvent.GetKey() == EKeys::Down)
+			{
+				if(SelectedSuggestion < Suggestions.Num() - 1)
+				{
+					// go one down, possibly from edit control to top
+					++SelectedSuggestion;
+				}
+				else
+				{
+					// back to edit control
+					SelectedSuggestion = -1;
+				}
+			}
+
+			MarkActiveSuggestion();
+
+			return FReply::Handled();
+		}
+		else if (KeyEvent.GetKey() == EKeys::Tab)
+		{
+			if (Suggestions.Num())
+			{
+				if (SelectedSuggestion >= 0 && SelectedSuggestion < Suggestions.Num())
+				{
+					MarkActiveSuggestion();
+					OnTextCommitted(InputText->GetText(), ETextCommit::OnEnter);
+				}
+				else
+				{
+					SelectedSuggestion = 0;
+					MarkActiveSuggestion();
+				}
+			}
+
+			return FReply::Handled();
+		}
+	}
+	else
+	{
+		if(KeyEvent.GetKey() == EKeys::Up)
+		{
+			TArray<FString> History;
+
+			IConsoleManager::Get().GetConsoleHistory(History);
+
+			SetSuggestions(History, true);
+			
+			if(Suggestions.Num())
+			{
+				SelectedSuggestion = Suggestions.Num() - 1;
+				MarkActiveSuggestion();
+			}
+
+			return FReply::Handled();
+		}
+	}
+
+	return FReply::Unhandled();
+}
+
+void SJavascriptConsoleInputBox::SetSuggestions(TArray<FString>& Elements, bool bInHistoryMode)
+{
+	FString SelectionText;
+	if (SelectedSuggestion >= 0 && SelectedSuggestion < Suggestions.Num())
+	{
+		SelectionText = *Suggestions[SelectedSuggestion];
+	}
+
+	SelectedSuggestion = -1;
+	Suggestions.Empty();
+	SelectedSuggestion = -1;
+
+	for(uint32 i = 0; i < (uint32)Elements.Num(); ++i)
+	{
+		Suggestions.Add(MakeShareable(new FString(Elements[i])));
+
+		if (Elements[i] == SelectionText)
+		{
+			SelectedSuggestion = i;
+		}
+	}
+
+	if(Suggestions.Num())
+	{
+		// Ideally if the selection box is open the output window is not changing it's window title (flickers)
+		SuggestionBox->SetIsOpen(true, false);
+		SuggestionListView->RequestScrollIntoView(Suggestions.Last());
+	}
+	else
+	{
+		SuggestionBox->SetIsOpen(false);
+	}
+}
+
+void SJavascriptConsoleInputBox::OnFocusLost( const FFocusEvent& InFocusEvent )
+{
+//	SuggestionBox->SetIsOpen(false);
+}
+
+void SJavascriptConsoleInputBox::MarkActiveSuggestion()
+{
+	bIgnoreUIUpdate = true;
+	if(SelectedSuggestion >= 0)
+	{
+		SuggestionListView->SetSelection(Suggestions[SelectedSuggestion]);
+		SuggestionListView->RequestScrollIntoView(Suggestions[SelectedSuggestion]);	// Ideally this would only scroll if outside of the view
+
+		InputText->SetText(FText::FromString(GetSelectionText()));
+	}
+	else
+	{
+		SuggestionListView->ClearSelection();
+	}
+	bIgnoreUIUpdate = false;
+}
+
+void SJavascriptConsoleInputBox::ClearSuggestions()
+{
+	SelectedSuggestion = -1;
+	SuggestionBox->SetIsOpen(false);
+	Suggestions.Empty();
+}
+
+FString SJavascriptConsoleInputBox::GetSelectionText() const
+{
+	FString ret = *Suggestions[SelectedSuggestion];
+	
+	ret = ret.Replace(TEXT("\t"), TEXT(""));
+
+	return ret;
+}
+
+/** Output log text marshaller to convert an array of FLogMessages into styled lines to be consumed by an FTextLayout */
+class FJavascriptLogTextLayoutMarshaller : public FBaseTextLayoutMarshaller
+{
+public:
+
+	static TSharedRef< FJavascriptLogTextLayoutMarshaller > Create(TArray< TSharedPtr<FLogMessage> > InMessages);
+
+	virtual ~FJavascriptLogTextLayoutMarshaller();
+	
+	// ITextLayoutMarshaller
+	virtual void SetText(const FString& SourceString, FTextLayout& TargetTextLayout) override;
+	virtual void GetText(FString& TargetString, const FTextLayout& SourceTextLayout) override;
+
+	bool AppendMessage(const TCHAR* InText, const ELogVerbosity::Type InVerbosity, const FName& InCategory);
+	void ClearMessages();
+	int32 GetNumMessages() const;
+
+protected:
+
+	FJavascriptLogTextLayoutMarshaller(TArray< TSharedPtr<FLogMessage> > InMessages);
+
+	void AppendMessageToTextLayout(const TSharedPtr<FLogMessage>& Message);
+
+	/** All log messages to show in the text box */
+	TArray< TSharedPtr<FLogMessage> > Messages;
+
+	FTextLayout* TextLayout;
+};
+
+TSharedRef< FJavascriptLogTextLayoutMarshaller > FJavascriptLogTextLayoutMarshaller::Create(TArray< TSharedPtr<FLogMessage> > InMessages)
+{
+	return MakeShareable(new FJavascriptLogTextLayoutMarshaller(MoveTemp(InMessages)));
+}
+
+FJavascriptLogTextLayoutMarshaller::~FJavascriptLogTextLayoutMarshaller()
+{
+}
+
+void FJavascriptLogTextLayoutMarshaller::SetText(const FString& SourceString, FTextLayout& TargetTextLayout)
+{
+	TextLayout = &TargetTextLayout;
+
+	for(const auto& Message : Messages)
+	{
+		AppendMessageToTextLayout(Message);
+	}
+}
+
+void FJavascriptLogTextLayoutMarshaller::GetText(FString& TargetString, const FTextLayout& SourceTextLayout)
+{
+	SourceTextLayout.GetAsText(TargetString);
+}
+
+bool FJavascriptLogTextLayoutMarshaller::AppendMessage(const TCHAR* InText, const ELogVerbosity::Type InVerbosity, const FName& InCategory)
+{
+	TArray< TSharedPtr<FLogMessage> > NewMessages;
+	if(SJavascriptLog::CreateLogMessages(InText, InVerbosity, InCategory, NewMessages))
+	{
+		const bool bWasEmpty = Messages.Num() == 0;
+		Messages.Append(NewMessages);
+
+		if(TextLayout)
+		{
+			// If we were previously empty, then we'd have inserted a dummy empty line into the document
+			// We need to remove this line now as it would cause the message indices to get out-of-sync with the line numbers, which would break auto-scrolling
+			if(bWasEmpty)
+			{
+				TextLayout->ClearLines();
+			}
+
+			// If we've already been given a text layout, then append these new messages rather than force a refresh of the entire document
+			for(const auto& Message : NewMessages)
+			{
+				AppendMessageToTextLayout(Message);
+			}
+		}
+		else
+		{
+			MakeDirty();
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
+void FJavascriptLogTextLayoutMarshaller::AppendMessageToTextLayout(const TSharedPtr<FLogMessage>& Message)
+{
+	const FTextBlockStyle& MessageTextStyle = FEditorStyle::Get().GetWidgetStyle<FTextBlockStyle>(Message->Style);
+
+	TSharedRef<FString> LineText = Message->Message;
+
+	TArray<TSharedRef<IRun>> Runs;
+	Runs.Add(FSlateTextRun::Create(FRunInfo(), LineText, MessageTextStyle));
+
+	TextLayout->AddLine(LineText, Runs);
+}
+
+void FJavascriptLogTextLayoutMarshaller::ClearMessages()
+{
+	Messages.Empty();
+	MakeDirty();
+}
+
+int32 FJavascriptLogTextLayoutMarshaller::GetNumMessages() const
+{
+	return Messages.Num();
+}
+
+FJavascriptLogTextLayoutMarshaller::FJavascriptLogTextLayoutMarshaller(TArray< TSharedPtr<FLogMessage> > InMessages)
+	: Messages(MoveTemp(InMessages))
+	, TextLayout(nullptr)
+{
+}
+
+BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
+void SJavascriptLog::Construct( const FArguments& InArgs )
+{
+	MessagesTextMarshaller = FJavascriptLogTextLayoutMarshaller::Create(MoveTemp(InArgs._Messages));
+
+	MessagesTextBox = SNew(SMultiLineEditableTextBox)
+		.Style(FEditorStyle::Get(), "Log.TextBox")
+		.TextStyle(FEditorStyle::Get(), "Log.Normal")
+		.ForegroundColor(FLinearColor::Gray)
+		.Marshaller(MessagesTextMarshaller)
+		.IsReadOnly(true)
+		.AlwaysShowScrollbars(true)
+		.OnVScrollBarUserScrolled(this, &SJavascriptLog::OnUserScrolled)
+		.ContextMenuExtender(this, &SJavascriptLog::ExtendTextBoxMenu);
+
+	ChildSlot
+	[
+		SNew(SVerticalBox)
+
+			// Output log area
+			+SVerticalBox::Slot()
+			.FillHeight(1)
+			[
+				MessagesTextBox.ToSharedRef()
+			]
+			// The console input boxa
+			+SVerticalBox::Slot()
+			.AutoHeight()
+			.Padding(FMargin(0.0f, 4.0f, 0.0f, 0.0f))
+			[
+				SNew( SJavascriptConsoleInputBox )
+				.OnConsoleCommandExecuted(this, &SJavascriptLog::OnConsoleCommandExecuted)
+
+				// Always place suggestions above the input line for the output log widget
+				.SuggestionListPlacement( MenuPlacement_AboveAnchor )
+			]
+	];
+	
+	GLog->AddOutputDevice(this);
+
+	bIsUserScrolled = false;
+	RequestForceScroll();
+}
+END_SLATE_FUNCTION_BUILD_OPTIMIZATION
+
+SJavascriptLog::~SJavascriptLog()
+{
+	if (GLog != NULL)
+	{
+		GLog->RemoveOutputDevice(this);
+	}
+}
+
+bool SJavascriptLog::CreateLogMessages( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category, TArray< TSharedPtr<FLogMessage> >& OutMessages )
+{
+	if (Verbosity == ELogVerbosity::SetColor)
+	{
+		// Skip Color Events
+		return false;
+	}
+	else
+	{
+		static FName NAME_Javascript("Javascript");
+		static FName NAME_JavascriptCmd("JavascriptCmd");
+
+		FName Style;
+		if (Category == NAME_JavascriptCmd)
+		{
+			Style = FName(TEXT("Log.Command"));
+		}
+		else if (Verbosity == ELogVerbosity::Error)
+		{
+			Style = FName(TEXT("Log.Error"));
+		}
+		else if (Verbosity == ELogVerbosity::Warning)
+		{
+			Style = FName(TEXT("Log.Warning"));
+		}
+		else
+		{
+			Style = FName(TEXT("Log.Normal"));
+		}
+
+		// Determine how to format timestamps
+		static ELogTimes::Type LogTimestampMode = ELogTimes::None;
+		if (UObjectInitialized() && !GExitPurge)
+		{
+			// Logging can happen very late during shutdown, even after the UObject system has been torn down, hence the init check above
+			LogTimestampMode = GetDefault<UEditorStyleSettings>()->LogTimestampMode;
+		}				
+
+		const int32 OldNumMessages = OutMessages.Num();
+
+		// handle multiline strings by breaking them apart by line
+		TArray<FTextRange> LineRanges;
+		FString CurrentLogDump = V;
+		FTextRange::CalculateLineRangesFromString(CurrentLogDump, LineRanges);
+
+		auto FormatLogLine = [](ELogVerbosity::Type Verbosity, const class FName& Category, const TCHAR* Message)
+		{	
+			FString Format;
+
+			if (Category == NAME_JavascriptCmd)
+			{
+				Format += TEXT("> ");
+			}
+			else if (Category == NAME_Javascript)
+			{
+				if (Verbosity == ELogVerbosity::Log)
+				{
+					Format += TEXT("< ");
+				}
+			}
+			else
+			{
+				Format += Category.ToString() + TEXT(": ");
+			}			
+
+			if (Verbosity != ELogVerbosity::Log)
+			{
+				Format += FString(VerbosityToString(Verbosity)) + TEXT(": ");
+			}			
+			
+			if (Message)
+			{
+				Format += Message;
+			}
+			return Format;
+		};
+
+		bool bIsFirstLineInMessage = true;
+		for (const FTextRange& LineRange : LineRanges)
+		{
+			if (!LineRange.IsEmpty())
+			{
+				FString Line = CurrentLogDump.Mid(LineRange.BeginIndex, LineRange.Len());
+				Line = Line.ConvertTabsToSpaces(4);
+				
+				OutMessages.Add(MakeShareable(new FLogMessage(MakeShareable(new FString((bIsFirstLineInMessage) ? FormatLogLine(Verbosity, Category, *Line) : Line)), Style)));
+
+				bIsFirstLineInMessage = false;
+			}
+		}
+
+		return OldNumMessages != OutMessages.Num();
+	}
+}
+
+void SJavascriptLog::Serialize( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category )
+{
+	if ( MessagesTextMarshaller->AppendMessage(V, Verbosity, Category) )
+	{
+		// Don't scroll to the bottom automatically when the user is scrolling the view or has scrolled it away from the bottom.
+		if( !bIsUserScrolled )
+		{
+			MessagesTextBox->ScrollTo(FTextLocation(MessagesTextMarshaller->GetNumMessages() - 1));
+		}
+	}
+}
+
+void SJavascriptLog::ExtendTextBoxMenu(FMenuBuilder& Builder)
+{
+	FUIAction ClearJavascriptLogAction(
+		FExecuteAction::CreateRaw( this, &SJavascriptLog::OnClearLog ),
+		FCanExecuteAction::CreateSP( this, &SJavascriptLog::CanClearLog )
+		);
+
+	Builder.AddMenuEntry(
+		NSLOCTEXT("JavascriptConsole", "ClearLogLabel", "Clear Log"), 
+		NSLOCTEXT("JavascriptConsole", "ClearLogTooltip", "Clears all log messages"), 
+		FSlateIcon(), 
+		ClearJavascriptLogAction
+		);
+}
+
+void SJavascriptLog::OnClearLog()
+{
+	// Make sure the cursor is back at the start of the log before we clear it
+	MessagesTextBox->GoTo(FTextLocation(0));
+
+	MessagesTextMarshaller->ClearMessages();
+	MessagesTextBox->Refresh();
+	bIsUserScrolled = false;
+}
+
+void SJavascriptLog::OnUserScrolled(float ScrollOffset)
+{
+	bIsUserScrolled = !FMath::IsNearlyEqual(ScrollOffset, 1.0f);
+}
+
+bool SJavascriptLog::CanClearLog() const
+{
+	return MessagesTextMarshaller->GetNumMessages() > 0;
+}
+
+void SJavascriptLog::OnConsoleCommandExecuted()
+{
+	RequestForceScroll();
+}
+
+void SJavascriptLog::RequestForceScroll()
+{
+	if(MessagesTextMarshaller->GetNumMessages() > 0)
+	{
+		MessagesTextBox->ScrollTo(FTextLocation(MessagesTextMarshaller->GetNumMessages() - 1));
+		bIsUserScrolled = false;
+	}
+}
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptLog.h b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptLog.h
new file mode 100644
index 0000000..73f7871
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Private/SJavascriptLog.h
@@ -0,0 +1,200 @@
+#pragma once
+
+class FJavascriptLogTextLayoutMarshaller;
+
+
+/**
+ * A single log message for the output log, holding a message and
+ * a style, for color and bolding of the message.
+ */
+struct FLogMessage
+{
+	TSharedRef<FString> Message;
+	FName Style;
+
+	FLogMessage(const TSharedRef<FString>& NewMessage, FName NewStyle = NAME_None)
+		: Message(NewMessage)
+		, Style(NewStyle)
+	{
+	}
+};
+
+
+/**
+ * Console input box with command-completion support
+ */
+class SJavascriptConsoleInputBox
+	: public SCompoundWidget
+{
+
+public:
+
+	SLATE_BEGIN_ARGS( SJavascriptConsoleInputBox )
+		: _SuggestionListPlacement( MenuPlacement_BelowAnchor )
+		{}
+
+		/** Where to place the suggestion list */
+		SLATE_ARGUMENT( EMenuPlacement, SuggestionListPlacement )
+
+		/** Called when a console command is executed */
+		SLATE_EVENT( FSimpleDelegate, OnConsoleCommandExecuted )
+	SLATE_END_ARGS()
+
+	/** Protected console input box widget constructor, called by Slate */
+	SJavascriptConsoleInputBox();
+
+	/**
+	 * Construct this widget.  Called by the SNew() Slate macro.
+	 *
+	 * @param	InArgs	Declaration used by the SNew() macro to construct this widget
+	 */
+	void Construct( const FArguments& InArgs );
+
+	/** Returns the editable text box associated with this widget.  Used to set focus directly. */
+	TSharedRef< SEditableTextBox > GetEditableTextBox()
+	{
+		return InputText.ToSharedRef();
+	}
+
+	/** SWidget interface */
+	virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override;
+
+protected:
+
+	virtual bool SupportsKeyboardFocus() const override { return true; }
+
+	// e.g. Tab or Key_Up
+	virtual FReply OnPreviewKeyDown( const FGeometry& MyGeometry, const FKeyEvent& KeyEvent ) override;
+
+	void OnFocusLost( const FFocusEvent& InFocusEvent ) override;
+
+	/** Handles entering in a command */
+	void OnTextCommitted(const FText& InText, ETextCommit::Type CommitInfo);
+
+	void OnTextChanged(const FText& InText);
+
+	/** Makes the widget for the suggestions messages in the list view */
+	TSharedRef<ITableRow> MakeSuggestionListItemWidget(TSharedPtr<FString> Message, const TSharedRef<STableViewBase>& OwnerTable);
+
+	void SuggestionSelectionChanged(TSharedPtr<FString> NewValue, ESelectInfo::Type SelectInfo);
+		
+	void SetSuggestions(TArray<FString>& Elements, bool bInHistoryMode);
+
+	void MarkActiveSuggestion();
+
+	void ClearSuggestions();
+
+	FString GetSelectionText() const;
+
+
+private:
+
+	/** Editable text widget */
+	TSharedPtr< SEditableTextBox > InputText;
+
+	/** history / auto completion elements */
+	TSharedPtr< SMenuAnchor > SuggestionBox;
+
+	/** All log messages stored in this widget for the list view */
+	TArray< TSharedPtr<FString> > Suggestions;
+
+	/** The list view for showing all log messages. Should be replaced by a full text editor */
+	TSharedPtr< SListView< TSharedPtr<FString> > > SuggestionListView;
+
+	/** Delegate to call when a console command is executed */
+	FSimpleDelegate OnConsoleCommandExecuted;
+
+	/** -1 if not set, otherwise index into Suggestions */
+	int32 SelectedSuggestion;
+
+	/** to prevent recursive calls in UI callback */
+	bool bIgnoreUIUpdate; 
+
+	TSharedPtr<STextComboBox> ContextComboBox;
+
+	TArray<TSharedPtr<FString> > ContextArray;
+	TSharedPtr<FString> TargetContext;
+};
+
+
+/**
+ * Widget which holds a list view of logs of the program output
+ * as well as a combo box for entering in new commands
+ */
+class SJavascriptLog 
+	: public SCompoundWidget, public FOutputDevice
+{
+
+public:
+
+	SLATE_BEGIN_ARGS( SJavascriptLog )
+		: _Messages()
+		{}
+		
+		/** All messages captured before this log window has been created */
+		SLATE_ARGUMENT( TArray< TSharedPtr<FLogMessage> >, Messages )
+
+	SLATE_END_ARGS()
+
+	/** Destructor for output log, so we can unregister from notifications */
+	~SJavascriptLog();
+
+	/**
+	 * Construct this widget.  Called by the SNew() Slate macro.
+	 *
+	 * @param	InArgs	Declaration used by the SNew() macro to construct this widget
+	 */
+	void Construct( const FArguments& InArgs );
+
+	/**
+	 * Creates FLogMessage objects from FOutputDevice log callback
+	 *
+	 * @param	V Message text
+	 * @param Verbosity Message verbosity
+	 * @param Category Message category
+	 * @param OutMessages Array to receive created FLogMessage messages
+	 *
+	 * @return true if any messages have been created, false otherwise
+	 */
+	static bool CreateLogMessages( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category, TArray< TSharedPtr<FLogMessage> >& OutMessages );
+
+protected:
+
+	virtual void Serialize( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category ) override;
+
+private:
+	/**
+	 * Extends the context menu used by the text box
+	 */
+	void ExtendTextBoxMenu(FMenuBuilder& Builder);
+
+	/**
+	 * Called when delete all is selected
+	 */
+	void OnClearLog();
+
+	/**
+	 * Called when the user scrolls the log window vertically
+	 */
+	void OnUserScrolled(float ScrollOffset);
+
+	/**
+	 * Called to determine whether delete all is currently a valid command
+	 */
+	bool CanClearLog() const;
+
+	/** Called when a console command is entered for this output log */
+	void OnConsoleCommandExecuted();
+
+	/** Request we immediately force scroll to the bottom of the log */
+	void RequestForceScroll();
+
+	/** Converts the array of messages into something the text box understands */
+	TSharedPtr< FJavascriptLogTextLayoutMarshaller > MessagesTextMarshaller;
+
+	/** The editable text showing all log messages */
+	TSharedPtr< SMultiLineEditableTextBox > MessagesTextBox;
+
+	/** True if the user has scrolled the window upwards */
+	bool bIsUserScrolled;
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptConsole/Public/JavascriptConsoleModule.h b/Plugins/UnrealJS/Source/JavascriptConsole/Public/JavascriptConsoleModule.h
new file mode 100644
index 0000000..2f131be
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptConsole/Public/JavascriptConsoleModule.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "SlateBasics.h"
+
+/** Style of the debug console */
+namespace EJavascriptConsoleStyle
+{
+	enum Type
+	{
+		/** Shows the debug console input line with tab completion only */
+		Compact,
+
+		/** Shows a scrollable log window with the input line on the bottom */
+		WithLog,
+	};
+};
+
+struct FJavascriptConsoleDelegates
+{
+	FSimpleDelegate OnFocusLost;
+	FSimpleDelegate OnConsoleCommandExecuted;
+};
+
+class FJavascriptConsoleModule : public IModuleInterface
+{
+public:
+	virtual void StartupModule();
+	virtual void ShutdownModule();
+
+	/** Generates a console input box widget.  Remember, this widget will become invalid if the
+	    output log DLL is unloaded on the fly. */
+	virtual TSharedRef< SWidget > MakeConsoleInputBox( TSharedPtr< SEditableTextBox >& OutExposedEditableTextBox ) const;
+
+	/** Opens a debug console in the specified window, if not already open */
+	virtual void ToggleJavascriptConsoleForWindow( const TSharedRef< SWindow >& Window, const EJavascriptConsoleStyle::Type InStyle, const FJavascriptConsoleDelegates& JavascriptConsoleDelegates );
+
+	/** Closes the debug console for the specified window */
+	virtual void CloseJavascriptConsole();
+
+
+private:
+
+	/** Weak pointer to a debug console that's currently open, if any */
+	TWeakPtr< SWidget > JavascriptConsole;
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetEditorToolkit.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetEditorToolkit.cpp
new file mode 100644
index 0000000..7598b77
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetEditorToolkit.cpp
@@ -0,0 +1,171 @@
+#include "JavascriptEditor.h"
+
+#if WITH_EDITOR
+#include "JavascriptAssetEditorToolkit.h"
+#include "JavascriptEditorTab.h"
+#include "JavascriptUICommands.h"
+#include "JavascriptUIExtender.h"
+#include "WorkspaceMenuStructureModule.h"
+#endif
+
+UJavascriptAssetEditorToolkit::UJavascriptAssetEditorToolkit(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer), bRegistered(false)
+{	
+}
+
+#if WITH_EDITOR
+void UJavascriptAssetEditorToolkit::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	Discard();
+}
+#endif
+
+void UJavascriptAssetEditorToolkit::Commit()
+{
+#if WITH_EDITOR
+	Discard();
+
+	IJavascriptEditorModule::Get().AddExtension(this);
+	
+	bRegistered = true;
+#endif
+}
+
+void UJavascriptAssetEditorToolkit::Discard()
+{
+#if WITH_EDITOR
+	if (bRegistered)
+	{
+		IJavascriptEditorModule::Get().RemoveExtension(this);
+	}
+
+	bRegistered = false;
+#endif
+}
+
+void UJavascriptAssetEditorToolkit::Refresh()
+{
+#if WITH_EDITOR
+	if (bRegistered)
+	{
+		Unregister();
+		Register();
+	}
+#endif
+}
+
+#if WITH_EDITOR
+class FAssetEditorToolkit_Javascript : public FAssetEditorToolkit
+{
+public:
+	UJavascriptAssetEditorToolkit* Outer;
+	UObject* Context;
+
+	FAssetEditorToolkit_Javascript(UJavascriptAssetEditorToolkit* InOuter)
+		: Outer(InOuter)
+	{
+		Outer->AddToRoot();
+	}
+
+	~FAssetEditorToolkit_Javascript()
+	{
+		Outer->RemoveFromRoot();
+	}
+
+	virtual void RegisterTabSpawners(const TSharedRef<class FTabManager>& TabManager) override
+	{		
+		FAssetEditorToolkit::RegisterTabSpawners(TabManager);
+
+		for (auto Tab : Outer->Tabs)
+		{
+			Tab->Register(TabManager, Context, WorkspaceMenu::GetMenuStructure().GetDeveloperToolsMiscCategory());
+		}
+	}
+
+	virtual void UnregisterTabSpawners(const TSharedRef<class FTabManager>& TabManager) override
+	{
+		FAssetEditorToolkit::UnregisterTabSpawners(TabManager);
+
+		for (auto Tab : Outer->Tabs)
+		{
+			Tab->Unregister(TabManager);
+		}
+	}
+
+	virtual FName GetToolkitFName() const override
+	{
+		return Outer->ToolkitFName;
+	}
+	virtual FText GetBaseToolkitName() const override
+	{
+		return Outer->BaseToolkitName;
+	}
+	virtual FText GetToolkitName() const override
+	{
+		return Outer->ToolkitName;
+	}
+	virtual FString GetWorldCentricTabPrefix() const override
+	{
+		return Outer->WorldCentricTabPrefix;
+	}	
+	virtual FLinearColor GetWorldCentricTabColorScale() const
+	{
+		return Outer->WorldCentricTabColorScale;
+	}
+	void Open(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor)
+	{
+		Context = InObjects[0];
+
+		const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone;
+
+		if (Outer->Commands)
+		{
+			Outer->Commands->Bind(&(GetToolkitCommands().Get()));
+		}
+				
+		TSharedPtr<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::FLayout::NewFromString(Outer->Layout.Replace(TEXT("#toolbartab"), *(GetToolbarTabId().ToString())));
+
+		// Initialize the asset editor and spawn nothing (dummy layout)
+		InitAssetEditor(Mode, EditWithinLevelEditor, Outer->ToolkitFName, StandaloneDefaultLayout.ToSharedRef(), /*bCreateDefaultStandaloneMenu=*/ true, /*bCreateDefaultToolbar=*/ true, InObjects);
+
+		if (Outer->MenuExtender)
+		{
+			auto Extender = Outer->MenuExtender->TakeMenuExtender(GetToolkitCommands());
+			if (Extender.IsValid())
+			{
+				AddMenuExtender(Extender);
+			}
+		}
+
+		if (Outer->ToolbarExtender)
+		{
+			auto Extender = Outer->ToolbarExtender->TakeToolbarExtender(GetToolkitCommands());
+			if (Extender.IsValid())
+			{
+				AddToolbarExtender(Extender);
+			}
+		}
+
+		RegenerateMenusAndToolbars();
+	}
+};
+
+void UJavascriptAssetEditorToolkit::Register()
+{	
+}
+
+void UJavascriptAssetEditorToolkit::Unregister()
+{
+}
+
+void UJavascriptAssetEditorToolkit::Open(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor)
+{
+	TSharedRef<FAssetEditorToolkit_Javascript> Editor(new FAssetEditorToolkit_Javascript(this));
+
+	Editor->Open(InObjects, EditWithinLevelEditor);
+
+	SpawnedEditors.Add(Editor);
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetEditorToolkit.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetEditorToolkit.h
new file mode 100644
index 0000000..a95a032
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetEditorToolkit.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#if WITH_EDITOR
+#include "AssetEditorToolkit.h"
+#endif
+
+#include "JavascriptAssetEditorToolkit.generated.h"
+
+class UJavascriptEditorTab;
+class UJavascriptUICommands;
+class UJavascriptUIExtender;
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptAssetEditorToolkit : public UObject, public IEditorExtension
+{
+	GENERATED_UCLASS_BODY()
+
+public:		
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FName ToolkitFName;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString Layout;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FText BaseToolkitName;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FText ToolkitName;	
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FLinearColor WorldCentricTabColorScale;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString WorldCentricTabPrefix;
+	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TArray<UJavascriptEditorTab*> Tabs;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	UJavascriptUICommands* Commands;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	UJavascriptUIExtender* MenuExtender;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	UJavascriptUIExtender* ToolbarExtender;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TArray<int32> TestArray;
+		
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Commit();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Discard();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Refresh();
+
+	bool bRegistered;
+
+#if WITH_EDITOR
+	virtual void Register() override;
+	virtual void Unregister() override;
+
+	virtual void BeginDestroy() override;
+
+	TArray<TWeakPtr<FAssetEditorToolkit>> SpawnedEditors;
+
+	void Open(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor);
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetTypeActions.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetTypeActions.cpp
new file mode 100644
index 0000000..24a0da9
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetTypeActions.cpp
@@ -0,0 +1,141 @@
+#include "JavascriptEditor.h"
+
+#if WITH_EDITOR
+#include "JavascriptAssetTypeActions.h"
+#include "AssetTypeActions_Base.h"
+#include "JavascriptAssetEditorToolkit.h"
+#include "JavascriptUIExtender.h"
+#endif
+
+UJavascriptAssetTypeActions::UJavascriptAssetTypeActions(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer), bRegistered(false)
+{	
+}
+
+#if WITH_EDITOR
+void UJavascriptAssetTypeActions::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	Discard();
+}
+#endif
+
+void UJavascriptAssetTypeActions::Commit()
+{
+#if WITH_EDITOR
+	Discard();
+
+	IJavascriptEditorModule::Get().AddExtension(this);
+	
+	bRegistered = true;
+#endif
+}
+
+void UJavascriptAssetTypeActions::Discard()
+{
+#if WITH_EDITOR
+	if (bRegistered)
+	{
+		IJavascriptEditorModule::Get().RemoveExtension(this);
+	}
+
+	bRegistered = false;
+#endif
+}
+
+void UJavascriptAssetTypeActions::Refresh()
+{
+#if WITH_EDITOR
+	if (bRegistered)
+	{
+		Unregister();
+		Register();
+	}
+#endif
+}
+
+#if WITH_EDITOR
+class FAssetTypeActions_Javascript : public FAssetTypeActions_Base
+{
+public:
+	UJavascriptAssetTypeActions* Owner;
+	FAssetTypeActions_Javascript(UJavascriptAssetTypeActions* InOwner)
+		: Owner(InOwner)
+	{
+	}
+
+	~FAssetTypeActions_Javascript()
+	{
+	}
+
+	// IAssetTypeActions interface
+	virtual FText GetName() const override
+	{
+		return Owner->ActionsName;
+	}
+
+	virtual FColor GetTypeColor() const override
+	{
+		return Owner->Color;
+	}
+	virtual UClass* GetSupportedClass() const override
+	{
+		return Owner->SupportedClass;
+	}
+
+	virtual bool HasActions(const TArray<UObject*>& InObjects) const override
+	{
+		return Owner->Actions != nullptr;
+	}
+
+	virtual void GetActions(const TArray<UObject*>& InObjects, FMenuBuilder& MenuBuilder) override
+	{
+		if (Owner->Actions)
+		{
+			Owner->Actions->BuildMenu(MenuBuilder);
+		}
+	}
+
+	virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor)
+	{
+		if (Owner->Editor)
+		{
+			Owner->Editor->Open(InObjects, EditWithinLevelEditor);
+		}
+		else
+		{
+			FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Not implemented")));
+		}		
+	}
+
+	virtual uint32 GetCategories() override
+	{
+		return EAssetTypeCategories::Gameplay;
+	}
+};
+
+void UJavascriptAssetTypeActions::Register()
+{
+	if (SupportedClass != nullptr)
+	{
+		Instance = MakeShareable(new FAssetTypeActions_Javascript(this));
+
+		IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
+		AssetTools.RegisterAssetTypeActions(Instance.ToSharedRef());
+	}	
+}
+
+void UJavascriptAssetTypeActions::Unregister()
+{
+	if (FModuleManager::Get().IsModuleLoaded("AssetTools"))
+	{
+		IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
+		if (Instance.IsValid())
+		{
+			AssetTools.UnregisterAssetTypeActions(Instance.ToSharedRef());
+		}		
+		Instance.Reset();
+	}
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetTypeActions.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetTypeActions.h
new file mode 100644
index 0000000..47c2e13
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptAssetTypeActions.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#if WITH_EDITOR
+#include "IAssetTypeActions.h"
+#endif
+
+#include "JavascriptAssetTypeActions.generated.h"
+
+class UJavascriptAssetEditorToolkit;
+class UJavascriptUIExtender;
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptAssetTypeActions : public UObject, public IEditorExtension
+{
+	GENERATED_UCLASS_BODY()
+
+public:		
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FText ActionsName;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FColor Color;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TSubclassOf<UObject> SupportedClass;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	UJavascriptAssetEditorToolkit* Editor;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	UJavascriptUIExtender* Actions;
+		
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Commit();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Discard();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Refresh();
+
+	bool bRegistered;
+
+#if WITH_EDITOR	
+	virtual void Register() override;
+	virtual void Unregister() override;
+
+	virtual void BeginDestroy() override;
+
+	TSharedPtr<IAssetTypeActions> Instance;
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.Build.cs b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.Build.cs
new file mode 100644
index 0000000..364759a
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.Build.cs
@@ -0,0 +1,69 @@
+using UnrealBuildTool;
+
+public class JavascriptEditor : ModuleRules
+{
+	public JavascriptEditor(TargetInfo Target)
+	{        
+        PublicDependencyModuleNames.AddRange(
+                new string[]
+				{
+					"Core",
+					"CoreUObject",
+					"Engine",
+					"Slate",                    
+                    "Landscape",
+				}
+            );	// @todo Mac: for some reason CoreUObject and Engine are needed to link in debug on Mac
+
+        if (UEBuildConfiguration.bBuildEditor == true)
+        {
+            PrivateIncludePaths.AddRange(
+                    new string[] {
+                        "Editor/GraphEditor/Private",
+				        "Editor/Kismet/Private",
+					    "Editor/GameplayAbilitiesEditor/Private",
+                        "Editor/LandscapeEditor/Private",
+                        "Developer/AssetTools/Private",
+				    }
+                );
+
+            PrivateDependencyModuleNames.AddRange(
+                new string[]
+				    {
+					    // ... add private dependencies that you statically link with here ...
+					    "Core",
+					    "CoreUObject",
+					    "Engine",
+					    "AssetTools",
+					    "ClassViewer",
+                        "InputCore",
+                        "PropertyEditor",
+					    "Slate",
+					    "SlateCore",
+                        "EditorStyle",                    
+					    "GraphEditor",
+					    "MainFrame",
+					    "UnrealEd",
+                        "WorkspaceMenuStructure",
+                        "V8",
+                        "UMG",
+                        "Foliage",
+                        "LandscapeEditor",
+				    }
+            );
+        }
+
+		// Uncomment if you are using Slate UI
+		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
+		
+		// Uncomment if you are using online features
+		// PrivateDependencyModuleNames.Add("OnlineSubsystem");
+		// if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64))
+		// {
+		//		if (UEBuildConfiguration.bCompileSteamOSS == true)
+		//		{
+		//			DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam");
+		//		}
+		// }
+	}
+}
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.cpp
new file mode 100644
index 0000000..14bfce9
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.cpp
@@ -0,0 +1,97 @@
+#include "JavascriptEditor.h"
+#include "AssetToolsModule.h"
+#include "JavascriptIsolate.h"
+#include "JavascriptContext.h"
+#include "JavascriptEditorTick.h"
+#include "IV8.h"
+
+class FJavascriptEditorModule : public IJavascriptEditorModule
+{
+	// Begin IModuleInterface
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+	// End IModuleInterface
+	
+	virtual void AddExtension(IEditorExtension* Extension) override;
+	virtual void RemoveExtension(IEditorExtension* Extension) override;
+	
+private:
+#if WITH_EDITOR
+	TArray<IEditorExtension*> Extensions;
+
+	UJavascriptEditorTick* Tick{ nullptr };
+	UJavascriptContext* JavascriptContext{ nullptr };
+
+	bool bRegistered{ false };
+
+	void Unregister();
+#endif
+};
+
+IMPLEMENT_MODULE(FJavascriptEditorModule, JavascriptEditor)
+
+void FJavascriptEditorModule::AddExtension(IEditorExtension* Extension)
+{
+#if WITH_EDITOR
+	Extensions.Add(Extension);
+	Extension->Register();
+#endif
+}
+
+void FJavascriptEditorModule::RemoveExtension(IEditorExtension* Extension)
+{
+#if WITH_EDITOR
+	Extensions.RemoveSingle(Extension);
+	Extension->Unregister();
+#endif
+}
+
+void FJavascriptEditorModule::StartupModule()
+{
+#if WITH_EDITOR	
+	auto Isolate = NewObject<UJavascriptIsolate>();
+	auto Context = Isolate->CreateContext();
+	
+	JavascriptContext = Context;
+	JavascriptContext->AddToRoot();
+
+	JavascriptContext->SetContextId(TEXT("Editor"));
+
+	Tick = NewObject<UJavascriptEditorTick>(JavascriptContext);
+	JavascriptContext->Expose(TEXT("Root"), Tick);
+	Tick->AddToRoot();
+
+	if(!IsRunningCommandlet())	Context->RunFile("editor.js");
+
+	bRegistered = true;
+	
+	FCoreDelegates::OnPreExit.AddRaw(this, &FJavascriptEditorModule::Unregister);
+#endif
+}
+
+void FJavascriptEditorModule::ShutdownModule()
+{
+#if WITH_EDITOR	
+	Unregister();
+#endif
+}
+
+#if WITH_EDITOR	
+void FJavascriptEditorModule::Unregister()
+{
+	if (!bRegistered) return;
+	bRegistered = false;
+
+	for (auto e : Extensions) { e->Unregister(); }
+	Extensions.Empty();
+
+	JavascriptContext->RunScript(TEXT("gc()"));
+
+	JavascriptContext->JavascriptContext.Reset();
+	
+	JavascriptContext->RemoveFromRoot();
+	Tick->RemoveFromRoot();		
+
+	CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);	
+}
+#endif
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.h
new file mode 100644
index 0000000..b686482
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditor.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#if WITH_EDITOR
+#include "UnrealEd.h"
+#endif
+
+// You should place include statements to your module's private header files here.  You only need to
+// add includes for headers that are used in most of your module's source files though.
+
+#include "Core.h"
+#include "Engine.h"
+
+#include "EditorStyle.h"
+
+#include "JavascriptEditorModule.h"
+
+#include "UMG.h"
+
+#if WITH_EDITOR
+#include "EditorStyle.h"
+#include "IDetailsView.h"
+#include "LandscapeDataAccess.h"
+#include "LandscapeEdit.h"
+#include "Editor/LandscapeEditor/Private/LandscapeEdMode.h"
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorLibrary.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorLibrary.cpp
new file mode 100644
index 0000000..49bab56
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorLibrary.cpp
@@ -0,0 +1,136 @@
+#include "JavascriptEditor.h"
+#include "JavascriptEditorLibrary.h"
+#include "Editor/LandscapeEditor/Private/LandscapeEdModeTools.h"
+
+ULandscapeInfo* UJavascriptEditorLibrary::GetLandscapeInfo(ALandscape* Landscape, bool bSpawnNewActor)
+{
+	return Landscape ? Landscape->GetLandscapeInfo(bSpawnNewActor) : nullptr;
+}
+
+void UJavascriptEditorLibrary::SetHeightmapDataFromMemory(ULandscapeInfo* LandscapeInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY)
+{
+	const int32 SizeX = (1 + MaxX - MinX);
+	const int32 SizeY = (1 + MaxY - MinY);
+
+	if (SizeX * SizeY * 2 == FArrayBufferAccessor::GetSize())
+	{
+		FHeightmapAccessor<false> Accessor(LandscapeInfo);
+		Accessor.SetData(MinX, MinY, MaxX, MaxY, (uint16*)FArrayBufferAccessor::GetData());
+	}	
+}
+
+void UJavascriptEditorLibrary::GetHeightmapDataToMemory(ULandscapeInfo* LandscapeInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY)
+{
+	const int32 SizeX = (1 + MaxX - MinX);
+	const int32 SizeY = (1 + MaxY - MinY);
+
+	if (SizeX * SizeY * 2 == FArrayBufferAccessor::GetSize())
+	{
+		auto Buffer = (uint16*)FArrayBufferAccessor::GetData();
+
+		FHeightmapAccessor<false> Accessor(LandscapeInfo);
+
+		TMap<FIntPoint, uint16> Data;
+		Accessor.GetData(MinX, MinY, MaxX, MaxY, Data);
+
+		FMemory::Memzero(Buffer, SizeX * SizeY * 2);
+
+		for (auto it = Data.CreateConstIterator(); it; ++it)
+		{
+			const auto& Point = it.Key();
+			Buffer[Point.X + Point.Y * SizeX] = it.Value();
+		}
+	}
+}
+
+void UJavascriptEditorLibrary::SetAlphamapDataFromMemory(ULandscapeInfo* LandscapeInfo, ULandscapeLayerInfoObject* LayerInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY, ELandscapeLayerPaintingRestriction::Type PaintingRestriction)
+{
+	if (LayerInfo == nullptr)
+	{
+		return;
+	}
+
+	const int32 SizeX = (1 + MaxX - MinX);
+	const int32 SizeY = (1 + MaxY - MinY);
+
+	if (SizeX * SizeY * 1 == FArrayBufferAccessor::GetSize())
+	{
+		FAlphamapAccessor<false,false> Accessor(LandscapeInfo, LayerInfo);
+		Accessor.SetData(MinX, MinY, MaxX, MaxY, (uint8*)FArrayBufferAccessor::GetData(), PaintingRestriction);
+	}
+}
+
+void UJavascriptEditorLibrary::GetAlphamapDataToMemory(ULandscapeInfo* LandscapeInfo, ULandscapeLayerInfoObject* LayerInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY)
+{
+	if (LayerInfo == nullptr)
+	{
+		return;
+	}
+
+	const int32 SizeX = (1 + MaxX - MinX);
+	const int32 SizeY = (1 + MaxY - MinY);
+
+	if (SizeX * SizeY * 1 == FArrayBufferAccessor::GetSize())
+	{
+		auto Buffer = (uint8*)FArrayBufferAccessor::GetData();
+
+		FAlphamapAccessor<false, false> Accessor(LandscapeInfo, LayerInfo);
+
+		TMap<FIntPoint, uint8> Data;
+		Accessor.GetData(MinX, MinY, MaxX, MaxY, Data);
+
+		FMemory::Memzero(Buffer, SizeX * SizeY);
+
+		for (auto it = Data.CreateConstIterator(); it; ++it)
+		{
+			const auto& Point = it.Key();
+			Buffer[Point.X + Point.Y * SizeX] = it.Value();
+		}
+	}
+}
+
+bool UJavascriptEditorLibrary::GetLandscapeExtent(ULandscapeInfo* LandscapeInfo, int32& MinX, int32& MinY, int32& MaxX, int32& MaxY) 
+{
+	if (!LandscapeInfo) return false;
+
+	return LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY);
+}
+
+ULandscapeLayerInfoObject* UJavascriptEditorLibrary::GetLayerInfoByName(ULandscapeInfo* LandscapeInfo, FName LayerName, ALandscapeProxy* Owner)
+{
+	return LandscapeInfo ? LandscapeInfo->GetLayerInfoByName(LayerName, Owner) : nullptr;
+}
+
+UWorld* UJavascriptEditorLibrary::GetEditorWorld(UEngine* Engine)
+{
+	for (const FWorldContext& Context : GEngine->GetWorldContexts())
+	{
+		if (Context.WorldType == EWorldType::Editor)
+		{
+			return Context.World();
+		}
+	}
+
+	return nullptr;
+}
+
+UWorld* UJavascriptEditorLibrary::GetPIEWorld(UEngine* Engine)
+{
+	for (const FWorldContext& Context : GEngine->GetWorldContexts())
+	{
+		if (Context.WorldType == EWorldType::PIE)
+		{
+			return Context.World();
+		}
+	}
+
+	return nullptr;
+}
+
+void UJavascriptEditorLibrary::RedrawAllViewports(UEditorEngine* Engine, bool bInvalidateHitProxies)
+{
+	if (Engine)
+	{
+		Engine->RedrawAllViewports(bInvalidateHitProxies);
+	}	
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorLibrary.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorLibrary.h
new file mode 100644
index 0000000..e30753a
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorLibrary.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "JavascriptEditorLibrary.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptEditorLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_BODY()
+
+#if WITH_EDITOR
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static ULandscapeInfo* GetLandscapeInfo(ALandscape* Landscape, bool bSpawnNewActor);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void SetHeightmapDataFromMemory(ULandscapeInfo* LandscapeInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void GetHeightmapDataToMemory(ULandscapeInfo* LandscapeInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY);	
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void SetAlphamapDataFromMemory(ULandscapeInfo* LandscapeInfo, ULandscapeLayerInfoObject* LayerInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY, ELandscapeLayerPaintingRestriction::Type PaintingRestriction  = ELandscapeLayerPaintingRestriction::None);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void GetAlphamapDataToMemory(ULandscapeInfo* LandscapeInfo, ULandscapeLayerInfoObject* LayerInfo, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static ULandscapeLayerInfoObject* GetLayerInfoByName(ULandscapeInfo* LandscapeInfo, FName LayerName, ALandscapeProxy* Owner = NULL);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static bool GetLandscapeExtent(ULandscapeInfo* LandscapeInfo, int32& MinX, int32& MinY, int32& MaxX, int32& MaxY);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static UWorld* GetEditorWorld(UEngine* Engine);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static UWorld* GetPIEWorld(UEngine* Engine);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void RedrawAllViewports(UEditorEngine* Engine, bool bInvalidateHitProxies);
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorMenu.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorMenu.cpp
new file mode 100644
index 0000000..7e1e15a
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorMenu.cpp
@@ -0,0 +1,123 @@
+#include "JavascriptEditor.h"
+#include "JavascriptEditorMenu.h"
+
+class JAVASCRIPTEDITOR_API SJavascriptMenu : public SVerticalBox
+{
+public:
+	SLATE_BEGIN_ARGS(SJavascriptMenu)
+	{}
+	SLATE_END_ARGS()
+
+	UJavascriptEditorMenu* Owner;
+	TSharedPtr<const FUICommandList> CommandList;
+
+	void Construct(const FArguments& InArgs)
+	{
+		SVerticalBox::Construct(SVerticalBox::FArguments());
+	}
+
+	virtual ~SJavascriptMenu()
+	{
+		Owner->Check(this);
+	}
+};
+
+UJavascriptEditorMenu::UJavascriptEditorMenu(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+#define LOCTEXT_NAMESPACE "SDeviceManager"
+
+#if WITH_EDITOR	
+namespace 
+{
+	static FMenuBarBuilder* CurrentMenuBarBuilder{ nullptr };
+	static UJavascriptEditorMenu* CurrentEditorMenu{ nullptr };
+	static TSharedPtr<const FUICommandList> CurrentCommandList;
+}
+void UJavascriptEditorMenu::Setup(TSharedRef<SVerticalBox> VerticalBox)
+{	
+	auto DockArea = StaticCastSharedRef<SJavascriptMenu>(VerticalBox);
+	auto CommandList = DockArea->CommandList.ToSharedRef();
+	CurrentCommandList = DockArea->CommandList;
+
+	FMenuBarBuilder MenuBarBuilder = FMenuBarBuilder(DockArea->CommandList);
+	CurrentMenuBarBuilder = &MenuBarBuilder;
+	CurrentEditorMenu = this;
+	OnHook.ExecuteIfBound("Menubar");	
+	CurrentMenuBarBuilder = nullptr;
+	CurrentEditorMenu = nullptr;
+	CurrentCommandList.Reset();
+
+	VerticalBox->ClearChildren();
+	VerticalBox->AddSlot()		
+		.FillHeight(1.0f)
+		[
+			MenuBarBuilder.MakeWidget()
+		];
+}
+
+void UJavascriptEditorMenu::AddPullDownMenu(const FName& Id, const FText& MenuLabel, const FText& Tooltip)
+{
+	auto Self = CurrentEditorMenu;
+	auto CommandList = CurrentCommandList.ToSharedRef();
+	CurrentMenuBarBuilder->AddPullDownMenu(
+		MenuLabel,
+		Tooltip,
+		FNewMenuDelegate::CreateLambda([Self, Id, CommandList](FMenuBuilder& MenuBuilder) {
+			MenuBuilder.PushCommandList(CommandList);
+			UJavascriptUIExtender::PushMenuBuilder(MenuBuilder);
+			Self->OnHook.ExecuteIfBound(Id);
+			UJavascriptUIExtender::Reset();
+		}),
+		Id
+	);
+}
+
+
+void UJavascriptEditorMenu::Refresh()
+{
+	for (auto Area : SpawnedMenus)
+	{
+		auto Ptr = Area.Pin();
+		if (Ptr.IsValid())
+		{
+			Setup(Ptr.ToSharedRef());
+		}
+	}
+}
+
+TSharedRef<SWidget> UJavascriptEditorMenu::RebuildWidget()
+{	
+	auto PrimaryArea = SNew(SJavascriptMenu);		
+	PrimaryArea->Owner = this;
+	PrimaryArea->CommandList = MakeShareable(new FUICommandList);
+
+	Setup(PrimaryArea);
+	SpawnedMenus.Add(PrimaryArea);
+
+	if (SpawnedMenus.Num() == 1)
+	{
+		AddToRoot();
+	}
+
+	return PrimaryArea;
+}
+
+void UJavascriptEditorMenu::Check(SVerticalBox* LastOne)
+{
+	for (int32 Index = SpawnedMenus.Num() - 1; Index >= 0; --Index)
+	{
+		if (!SpawnedMenus[Index].IsValid())
+		{
+			SpawnedMenus.RemoveAt(Index);
+		}
+	}
+
+	if (SpawnedMenus.Num() == 0 || SpawnedMenus.Num() == 1 && SpawnedMenus[0].Pin().Get() == LastOne)
+	{
+		RemoveFromRoot();
+	}
+}
+#endif
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorMenu.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorMenu.h
new file mode 100644
index 0000000..65db2f3
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorMenu.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "JavascriptEditorMenu.generated.h"
+
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptEditorMenu : public UWidget
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FOnHook, FName, Hook);
+
+	UPROPERTY()
+	FOnHook OnHook;
+
+#if WITH_EDITOR
+	TArray<TWeakPtr<SVerticalBox>> SpawnedMenus;
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void AddPullDownMenu(const FName& Id, const FText& MenuLabel, const FText& Tooltip);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Refresh();
+	
+	void Commit();
+	void Setup(TSharedRef<SVerticalBox> VerticalBox);
+	void Check(SVerticalBox* LastOne);
+
+	virtual TSharedRef<SWidget> RebuildWidget();
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorModule.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorModule.h
new file mode 100644
index 0000000..5614e01
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorModule.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "ModuleManager.h"
+
+struct IEditorExtension
+{
+#if WITH_EDITOR
+	virtual void Register() = 0;
+	virtual void Unregister() = 0;
+#endif
+};
+
+/**
+ * The public interface to this module
+ */
+class IJavascriptEditorModule : public IModuleInterface
+{
+
+public:
+
+	/**
+	 * Singleton-like access to this module's interface.  This is just for convenience!
+	 * Beware of calling this during the shutdown phase, though.  Your module might have been unloaded already.
+	 *
+	 * @return Returns singleton instance, loading the module on demand if needed
+	 */
+	static inline IJavascriptEditorModule& Get()
+	{
+		return FModuleManager::LoadModuleChecked< IJavascriptEditorModule >("JavascriptEditor");
+	}
+
+	/**
+	 * Checks to see if this module is loaded and ready.  It is only valid to call Get() if IsAvailable() returns true.
+	 *
+	 * @return True if the module is loaded and ready to use
+	 */
+	static inline bool IsAvailable()
+	{
+		return FModuleManager::Get().IsModuleLoaded( "JavascriptEditor" );
+	}
+
+	virtual void AddExtension(IEditorExtension* Extension) = 0;
+	virtual void RemoveExtension(IEditorExtension* Extension) = 0;
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorStyle.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorStyle.cpp
new file mode 100644
index 0000000..369b69b
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorStyle.cpp
@@ -0,0 +1,168 @@
+#include "JavascriptEditor.h"
+#include "JavascriptEditorStyle.h"
+
+UJavascriptEditorStyle::UJavascriptEditorStyle(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+static UStruct* Struct_SlateColor;
+static UStruct* Struct_ButtonStyle;
+static UStruct* Struct_SlateBrush;
+static UStruct* Struct_TextBlockStyle;
+static UStruct* Struct_EditableTextStyle;
+static UStruct* Struct_EditableTextBoxStyle;
+static UStruct* Struct_CheckBoxStyle;
+static UStruct* Struct_ComboBoxStyle;
+static UStruct* Struct_ComboButtonStyle;
+
+void Prepare()
+{
+	if (Struct_SlateColor) return;	
+
+	auto prepare = [&](UStruct*& Out, const TCHAR* Name)
+	{
+		UStructProperty* Prop = FindField<UStructProperty>(UJavascriptEditorStyle::StaticClass(), Name);
+		Out = Prop->Struct;
+	};
+
+	prepare(Struct_SlateColor, TEXT("SlateColor"));
+	prepare(Struct_ButtonStyle, TEXT("ButtonStyle"));
+	prepare(Struct_SlateBrush, TEXT("SlateBrush"));
+	prepare(Struct_TextBlockStyle, TEXT("TextBlockStyle"));
+	prepare(Struct_EditableTextStyle, TEXT("EditableTextStyle"));
+	prepare(Struct_EditableTextBoxStyle, TEXT("EditableTextBoxStyle"));
+	prepare(Struct_CheckBoxStyle, TEXT("CheckBoxStyle"));
+	prepare(Struct_ComboBoxStyle, TEXT("ComboBoxStyle"));
+	prepare(Struct_ComboButtonStyle, TEXT("ComboButtonStyle"));
+}
+
+FSlateColor Fixup(const FSlateColor& Src)
+{
+	Prepare();
+
+	if (Src.IsColorSpecified())
+	{
+		return FSlateColor(Src.GetSpecifiedColor());
+	}
+	else
+	{
+		return Src;
+	}
+}
+
+void FixupStruct(UStruct* Struct, void* Dest, const void* Src)
+{
+	if (Struct == Struct_SlateColor)
+	{
+		*((FSlateColor*)Dest) = Fixup(*(FSlateColor*)Src);
+		return;
+	}
+
+	for (TFieldIterator<UProperty> PropertyIt(Struct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
+	{
+		auto Property = *PropertyIt;
+		if (UStructProperty* p = Cast<UStructProperty>(Property))
+		{
+			FixupStruct(p->Struct, p->ContainerPtrToValuePtr<void>(Dest), p->ContainerPtrToValuePtr<void>(Src));
+		}
+	}
+}
+
+FSlateBrush Fixup(const FSlateBrush& Src)
+{
+	Prepare();
+
+	FSlateBrush Dest = Src;
+	FixupStruct(Struct_SlateBrush, &Dest, &Src);
+	return Dest;
+}
+
+template <typename T>
+T FixupWidgetStyle(UStruct* Struct, const FName& StyleName)
+{
+	Prepare();
+
+	const auto& Src = FEditorStyle::Get().GetWidgetStyle<T>(StyleName);
+	T Dest = Src;
+	FixupStruct(Struct, &Dest, &Src);
+	return Dest;
+}
+
+#if WITH_EDITOR
+FButtonStyle UJavascriptEditorStyle::GetButtonStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FButtonStyle>(Struct_ButtonStyle, StyleName);
+}
+
+FTextBlockStyle UJavascriptEditorStyle::GetTextBlockStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FTextBlockStyle>(Struct_TextBlockStyle, StyleName);
+}
+
+FEditableTextStyle UJavascriptEditorStyle::GetEditableTextStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FEditableTextStyle>(Struct_EditableTextStyle, StyleName);
+}
+
+FEditableTextBoxStyle UJavascriptEditorStyle::GetEditableTextBoxStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FEditableTextBoxStyle>(Struct_EditableTextBoxStyle, StyleName);
+}
+
+FCheckBoxStyle UJavascriptEditorStyle::GetCheckBoxStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FCheckBoxStyle>(Struct_CheckBoxStyle, StyleName);
+}
+
+FComboBoxStyle UJavascriptEditorStyle::GetComboBoxStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FComboBoxStyle>(Struct_ComboBoxStyle, StyleName);
+}
+
+FComboButtonStyle UJavascriptEditorStyle::GetComboButtonStyle(const FName& StyleName)
+{
+	return FixupWidgetStyle<FComboButtonStyle>(Struct_ComboButtonStyle, StyleName);
+}
+
+FMargin UJavascriptEditorStyle::GetMargin(const FName& StyleName)
+{
+	return FEditorStyle::Get().GetMargin(StyleName);
+}
+
+FSlateColor UJavascriptEditorStyle::GetSlateColor(const FName& StyleName)
+{
+	return Fixup(FEditorStyle::Get().GetSlateColor(StyleName));
+}
+
+FSlateBrush UJavascriptEditorStyle::GetBrush(const FName& StyleName)
+{
+	auto Brush = FEditorStyle::Get().GetBrush(StyleName);
+	return Brush ? Fixup(*Brush) : FSlateBrush();
+}
+
+FLinearColor UJavascriptEditorStyle::GetColor(const FName& StyleName)
+{
+	return FEditorStyle::Get().GetColor(StyleName);
+}
+
+FVector2D UJavascriptEditorStyle::GetVector(const FName& StyleName)
+{
+	return FEditorStyle::Get().GetVector(StyleName);
+}
+
+float UJavascriptEditorStyle::GetFloat(const FName& StyleName)
+{
+	return FEditorStyle::Get().GetFloat(StyleName);
+}
+
+FSlateFontInfo UJavascriptEditorStyle::GetFontStyle(const FName& StyleName)
+{
+	return FEditorStyle::Get().GetFontStyle(StyleName);
+}
+
+FSlateSound UJavascriptEditorStyle::GetSound(const FName& StyleName)
+{
+	return FEditorStyle::Get().GetSound(StyleName);
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorStyle.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorStyle.h
new file mode 100644
index 0000000..6b9b691
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorStyle.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "JavascriptEditorStyle.generated.h"
+
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptEditorStyle : public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:		
+
+#if WITH_EDITOR
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FButtonStyle GetButtonStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FTextBlockStyle GetTextBlockStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FEditableTextStyle GetEditableTextStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FEditableTextBoxStyle GetEditableTextBoxStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FCheckBoxStyle GetCheckBoxStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FComboBoxStyle GetComboBoxStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FComboButtonStyle GetComboButtonStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FMargin GetMargin(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FSlateColor GetSlateColor(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FSlateBrush GetBrush(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FLinearColor GetColor(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FVector2D GetVector(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static float GetFloat(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FSlateFontInfo GetFontStyle(const FName& StyleName);
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	static FSlateSound GetSound(const FName& StyleName);
+#endif
+
+	UPROPERTY()
+	FSlateColor SlateColor;	
+
+	UPROPERTY()
+	FSlateBrush SlateBrush;
+
+	UPROPERTY()
+	FButtonStyle ButtonStyle;
+	
+	UPROPERTY()
+	FTextBlockStyle TextBlockStyle;
+
+	UPROPERTY()
+	FEditableTextStyle EditableTextStyle;
+
+	UPROPERTY()
+	FEditableTextBoxStyle EditableTextBoxStyle;
+
+	UPROPERTY()
+	FCheckBoxStyle CheckBoxStyle;
+
+	UPROPERTY()
+	FComboBoxStyle ComboBoxStyle;
+
+	UPROPERTY()
+	FComboButtonStyle ComboButtonStyle;
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTab.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTab.cpp
new file mode 100644
index 0000000..f042edb
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTab.cpp
@@ -0,0 +1,156 @@
+#include "JavascriptEditor.h"
+#include "JavascriptEditorTab.h"
+#include "WorkspaceMenuStructureModule.h"
+
+UJavascriptEditorTab::UJavascriptEditorTab(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer), bRegistered(false), bIsNomad(true)
+{	
+}
+
+TSharedPtr<SDockTab> UJavascriptEditorTab::MajorTab;
+
+#if WITH_EDITOR
+void UJavascriptEditorTab::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	Discard();
+}
+#endif
+
+void UJavascriptEditorTab::Commit()
+{
+#if WITH_EDITOR
+	Discard();
+
+	IJavascriptEditorModule::Get().AddExtension(this);
+	
+	bRegistered = true;
+#endif
+}
+
+void UJavascriptEditorTab::Discard()
+{
+#if WITH_EDITOR
+	if (bRegistered)
+	{
+		IJavascriptEditorModule::Get().RemoveExtension(this);
+	}
+
+	bRegistered = false;
+#endif
+}
+
+#if WITH_EDITOR
+struct FHackFindDocktab
+{
+	UWidget* Widget;
+	TWeakPtr<SDockTab> DockTab;
+};
+
+TArray<FHackFindDocktab> GHackFindDocktabs;
+
+TSharedPtr<SDockTab> UJavascriptEditorTab::FindDocktab(UWidget* Widget)
+{
+	if (MajorTab.IsValid()) return MajorTab;
+
+	for (auto& entry : GHackFindDocktabs)
+	{
+		for (auto p = Widget; p; p = p->GetParent())
+		{
+			if (p == entry.Widget)
+			{
+				return entry.DockTab.Pin();
+			}
+		}
+	}
+
+	return TSharedPtr<SDockTab>();
+}
+
+UWidget* UJavascriptEditorTab::TakeWidget(UObject* Context)
+{
+	if (OnSpawnTab.IsBound())
+	{
+		UWidget* Widget = OnSpawnTab.Execute(Context);
+		if (Widget) return Widget;
+	}
+	return NewObject<UButton>();
+}
+
+void UJavascriptEditorTab::Register(TSharedRef<FTabManager> TabManager, UObject* Context, TSharedRef<FWorkspaceItem> Group)
+{
+	FSlateIcon Icon(FEditorStyle::GetStyleSetName(), "DeviceDetails.Tabs.ProfileEditor");
+
+	auto Lambda = FOnSpawnTab::CreateLambda([this, Context](const FSpawnTabArgs& SpawnTabArgs){
+		auto Widget = this->TakeWidget(Context);
+
+		const TSharedRef<SDockTab> MajorTab = SNew(SDockTab)
+			.TabRole(ETabRole(Role.GetValue()))
+			.OnTabClosed_Lambda([Widget](TSharedRef<SDockTab> ClosedTab){
+				Widget->RemoveFromRoot();
+			});
+
+		auto OldTab = UJavascriptEditorTab::MajorTab;
+		UJavascriptEditorTab::MajorTab = MajorTab;
+
+		Widget->AddToRoot();
+		MajorTab->SetContent(Widget->TakeWidget());
+		auto entry = new(GHackFindDocktabs)FHackFindDocktab;
+		entry->Widget = Widget;
+		entry->DockTab = MajorTab;
+		
+		UJavascriptEditorTab::MajorTab = OldTab;
+
+		SpawnedTabs.Add(MajorTab);
+
+		return MajorTab;
+	});	
+
+	auto& SpawnerEntry = bIsNomad && TabManager == FGlobalTabmanager::Get() ? FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TabId, Lambda) : TabManager->RegisterTabSpawner(TabId, Lambda);
+	SpawnerEntry
+		.SetDisplayName(DisplayName)
+		.SetIcon(Icon)
+		.SetGroup(Group);	
+}
+
+void UJavascriptEditorTab::Unregister(TSharedRef<FTabManager> TabManager)
+{
+	if (bIsNomad && TabManager == FGlobalTabmanager::Get())
+	{
+		FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TabId);
+	}
+	else
+	{
+		TabManager->UnregisterTabSpawner(TabId);
+	}	
+}
+
+void UJavascriptEditorTab::Register()
+{
+	Register(FGlobalTabmanager::Get(), nullptr, WorkspaceMenu::GetMenuStructure().GetDeveloperToolsMiscCategory());
+}
+
+void UJavascriptEditorTab::Unregister()
+{
+	Unregister(FGlobalTabmanager::Get());
+}
+#endif
+
+void UJavascriptEditorTab::Refresh()
+{
+#if WITH_EDITOR
+	for (auto& tab : SpawnedTabs)
+	{		
+		if (tab.IsValid())
+		{
+			tab.Pin()->RequestCloseTab();
+			SpawnedTabs.Empty();
+			FGlobalTabmanager::Get()->InvokeTab(TabId);
+			return;
+		}
+	}
+
+	SpawnedTabs.Empty();	
+#endif
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTab.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTab.h
new file mode 100644
index 0000000..b2699cd
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTab.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "SDockTab.h"
+#include "JavascriptEditorTab.generated.h"
+
+UENUM()
+namespace EJavascriptTabRole
+{
+	enum Type
+	{
+		MajorTab,
+		PanelTab,
+		NomadTab,
+		DocumentTab		
+	};
+}
+
+class UJavascriptEditorTabManager;
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptEditorTab : public UObject, public IEditorExtension
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(UWidget*, FSpawnTab, UObject*, Context);
+
+	UPROPERTY()
+	FSpawnTab OnSpawnTab;	
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FName TabId;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FText DisplayName;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	bool bIsNomad;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TEnumAsByte<EJavascriptTabRole::Type> Role;
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Commit();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Discard();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Refresh();
+
+	bool bRegistered;
+
+#if WITH_EDITOR
+	UWidget* TakeWidget(UObject* Context);
+
+	TArray<TWeakPtr<SDockTab>> SpawnedTabs;
+
+	virtual void Register() override;
+	virtual void Unregister() override;
+
+	virtual void BeginDestroy() override;
+
+	void Register(TSharedRef<FTabManager> TabManager, UObject* Context, TSharedRef<FWorkspaceItem> Group);
+	void Unregister(TSharedRef<FTabManager> TabManager);
+
+	static TSharedPtr<SDockTab> MajorTab;
+	static TSharedPtr<SDockTab> FindDocktab(UWidget* Widget);
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTabManager.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTabManager.cpp
new file mode 100644
index 0000000..f8a1cca
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTabManager.cpp
@@ -0,0 +1,120 @@
+#include "JavascriptEditor.h"
+#include "JavascriptEditorTabManager.h"
+
+#define LOCTEXT_NAMESPACE "JavascriptTabManager"
+
+class JAVASCRIPTEDITOR_API SPrimaryDockingArea : public SVerticalBox
+{
+public:
+	SLATE_BEGIN_ARGS(SPrimaryDockingArea)
+	{}
+	SLATE_END_ARGS()
+
+	UJavascriptEditorTabManager* Owner;
+
+	void Construct(const FArguments& InArgs)
+	{
+		SVerticalBox::Construct(SVerticalBox::FArguments());
+	}
+
+	virtual ~SPrimaryDockingArea()
+	{
+		Owner->Check(this);
+	}
+	
+	TSharedPtr<FTabManager> TabManager;
+	TWeakPtr<SDockTab> DockTab;
+};
+
+UJavascriptEditorTabManager::UJavascriptEditorTabManager(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+#if WITH_EDITOR	
+void UJavascriptEditorTabManager::Setup(TSharedRef<SVerticalBox> VerticalBox)
+{	
+	auto DockArea = StaticCastSharedRef<SPrimaryDockingArea>(VerticalBox);
+
+	TSharedRef<SDockTab> ConstructUnderMajorTab = DockArea->DockTab.Pin().ToSharedRef();
+	TSharedPtr<SWindow> ConstructUnderWindow;
+
+	auto TabManager = FGlobalTabmanager::Get()->NewTabManager(ConstructUnderMajorTab);
+	auto AppMenuGroup = TabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("DeviceManagerMenuGroupName", "Device Manager"));
+
+	TabManager->UnregisterAllTabSpawners();
+
+	for (auto Tab : Tabs)
+	{
+		Tab->Register(TabManager, nullptr, AppMenuGroup);
+	}
+
+	auto CachedLayout = FTabManager::FLayout::NewFromString(Layout);	
+
+	VerticalBox->ClearChildren();
+
+	if (CachedLayout.IsValid())
+	{
+		VerticalBox->AddSlot()
+			.FillHeight(1.0f)
+			[
+				TabManager->RestoreFrom(CachedLayout.ToSharedRef(), ConstructUnderWindow).ToSharedRef()
+			];
+	}	
+
+	DockArea->TabManager = TabManager;
+}
+
+//void UJavascriptEditorTabManager::Refresh()
+//{
+//	for (auto Area : SpawnedAreas)
+//	{
+//		auto Ptr = Area.Pin();
+//		if (Ptr.IsValid())
+//		{
+//			Setup(Ptr.ToSharedRef());
+//		}
+//	}
+//}
+
+TSharedRef<SWidget> UJavascriptEditorTabManager::RebuildWidget()
+{	
+	auto DockTab = UJavascriptEditorTab::FindDocktab(this);
+	if (DockTab.IsValid())
+	{
+		auto PrimaryArea = SNew(SPrimaryDockingArea);
+		PrimaryArea->DockTab = DockTab;
+		PrimaryArea->Owner = this;
+
+		Setup(PrimaryArea);
+		SpawnedAreas.Add(PrimaryArea);
+
+		if (SpawnedAreas.Num() == 1)
+		{
+			AddToRoot();
+		}
+
+		return PrimaryArea;
+	}
+	else
+	{
+		return SNew(SButton);
+	}	
+}
+
+void UJavascriptEditorTabManager::Check(SVerticalBox* LastOne)
+{
+	for (int32 Index = SpawnedAreas.Num() - 1; Index >= 0; --Index)
+	{
+		if (!SpawnedAreas[Index].IsValid())
+		{
+			SpawnedAreas.RemoveAt(Index);
+		}
+	}
+
+	if (SpawnedAreas.Num() == 0 || SpawnedAreas.Num() == 1 && SpawnedAreas[0].Pin().Get() == LastOne)
+	{
+		RemoveFromRoot();
+	}
+}
+#endif
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTabManager.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTabManager.h
new file mode 100644
index 0000000..61201bb
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTabManager.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "JavascriptEditorTab.h"
+#include "JavascriptEditorTabManager.generated.h"
+
+class UJavascriptEditorTabManager;
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptEditorTabManager : public UWidget
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString Layout;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TArray<UJavascriptEditorTab*> Tabs;
+
+#if WITH_EDITOR
+	TArray<TWeakPtr<SVerticalBox>> SpawnedAreas;
+	
+	void Commit();
+	void Setup(TSharedRef<SVerticalBox> VerticalBox);
+	void Check(SVerticalBox* LastOne);
+
+	virtual TSharedRef<SWidget> RebuildWidget();
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTick.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTick.cpp
new file mode 100644
index 0000000..d159697
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTick.cpp
@@ -0,0 +1,57 @@
+#include "JavascriptEditor.h"
+#include "JavascriptEditorTick.h"
+
+#if WITH_EDITOR
+class FJavascriptEditorTick : public FTickableEditorObject
+{
+public:
+	FJavascriptEditorTick(UJavascriptEditorTick* InObject)
+		: Object(InObject)
+	{}
+
+	UJavascriptEditorTick* Object;
+
+	virtual void Tick(float DeltaTime) override
+	{
+		Object->OnTick.ExecuteIfBound(DeltaTime);
+	}
+
+	virtual bool IsTickable() const override
+	{
+		return true;
+	}
+
+	virtual TStatId GetStatId() const override
+	{
+		RETURN_QUICK_DECLARE_CYCLE_STAT(FJavascriptEditorTick, STATGROUP_Tickables);
+	}
+};
+#endif
+
+UJavascriptEditorTick::UJavascriptEditorTick(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+#if WITH_EDITOR
+	if (!IsTemplate(RF_ClassDefaultObject))
+	{
+		Tickable = new FJavascriptEditorTick(this);
+	}
+#endif
+}
+
+#if WITH_EDITOR
+void UJavascriptEditorTick::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	if (Tickable)
+	{
+		delete Tickable;
+	}
+}
+
+UEditorEngine* UJavascriptEditorTick::GetEngine()
+{
+	return Cast<UEditorEngine>(GEngine);
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTick.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTick.h
new file mode 100644
index 0000000..19a7245
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptEditorTick.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#if WITH_EDITOR
+#include "TickableEditorObject.h"
+#endif
+#include "JavascriptEditorTick.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptEditorTick : public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+#if WITH_EDITOR
+	FTickableEditorObject* Tickable;
+
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FJavascriptEditorTickSignature, float, DeltaSeconds);
+
+	UPROPERTY()
+	FJavascriptEditorTickSignature OnTick;
+
+	UFUNCTION()
+	UEditorEngine* GetEngine();
+
+	virtual void BeginDestroy() override;
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptNotification.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptNotification.cpp
new file mode 100644
index 0000000..46f5fce
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptNotification.cpp
@@ -0,0 +1,98 @@
+#include "JavascriptEditor.h"
+#include "JavascriptNotification.h"
+
+#define LOCTEXT_NAMESPACE "UMG"
+
+UJavascriptNotification::UJavascriptNotification(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer),
+FadeInDuration(0.5f),
+FadeOutDuration(2.0f),
+ExpireDuration(1.0f),
+bUseThrobber(true),
+bUseSuccessFailIcons(true),
+bUseLargeFont(true),
+bFireAndForget(true),
+CheckBoxState(ECheckBoxState::Unchecked),
+bAllowThrottleWhenFrameRateIsLow(true)
+{}
+
+ECheckBoxState UJavascriptNotification::GetCheckBoxState() const
+{
+	return CheckBoxState;
+}
+
+void UJavascriptNotification::Fire()
+{
+#if WITH_EDITOR
+	FNotificationInfo Info(Text);
+
+	if (bUseImage)
+	{
+		Info.Image = &Image;
+	}
+	
+	Info.FadeInDuration = FadeInDuration;
+	Info.FadeOutDuration = FadeOutDuration;
+	Info.ExpireDuration = ExpireDuration;
+	Info.bUseThrobber = bUseThrobber;
+	Info.bUseSuccessFailIcons = bUseSuccessFailIcons;
+	Info.bUseLargeFont = bUseLargeFont;
+	Info.bFireAndForget = bFireAndForget;
+
+	if (CheckBoxStateChanged.IsBound())
+	{
+		Info.CheckBoxState.BindUObject(this, &UJavascriptNotification::GetCheckBoxState);
+		Info.CheckBoxStateChanged.BindLambda([this](ECheckBoxState State){
+			CheckBoxStateChanged.ExecuteIfBound(State);
+		});
+		Info.CheckBoxText.Set(CheckBoxText);
+	}
+
+	if (Hyperlink.IsBound())
+	{
+		Info.Hyperlink.BindLambda([this]{
+			Hyperlink.ExecuteIfBound();
+		});
+		Info.HyperlinkText.Set(HyperlinkText);
+	}
+	
+	Info.bAllowThrottleWhenFrameRateIsLow = bAllowThrottleWhenFrameRateIsLow;
+
+	Item = FSlateNotificationManager::Get().AddNotification(Info);		
+#endif
+}
+
+void UJavascriptNotification::Success()
+{
+#if WITH_EDITOR
+	if (Item.IsValid()) Item->SetCompletionState(SNotificationItem::CS_Success);
+#endif
+}
+
+void UJavascriptNotification::Pending()
+{
+#if WITH_EDITOR
+	if (Item.IsValid()) Item->SetCompletionState(SNotificationItem::CS_Pending);
+#endif
+}
+
+void UJavascriptNotification::Fail()
+{
+#if WITH_EDITOR
+	if (Item.IsValid()) Item->SetCompletionState(SNotificationItem::CS_Fail);
+#endif
+}
+
+void UJavascriptNotification::Fadeout()
+{
+#if WITH_EDITOR
+	if (Item.IsValid()) Item->Fadeout();
+#endif
+}
+
+void UJavascriptNotification::Reset()
+{
+#if WITH_EDITOR
+	Item.Reset();
+#endif
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptNotification.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptNotification.h
new file mode 100644
index 0000000..86671bc
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptNotification.h
@@ -0,0 +1,109 @@
+#pragma once
+
+#include "SNotificationList.h"
+#include "NotificationManager.h"
+#include "JavascriptNotification.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptNotification : public UObject
+{
+	GENERATED_UCLASS_BODY()
+	
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FCheckDelegate, ECheckBoxState, State);
+	DECLARE_DYNAMIC_DELEGATE(FSimpleDelegate);
+
+#if WITH_EDITOR
+	TSharedPtr<SNotificationItem> Item;
+#endif
+
+	UFUNCTION(BlueprintCallable, Category = "JavascriptNotification")
+	void Fire();
+
+	UFUNCTION(BlueprintCallable, Category = "JavascriptNotification")
+	void Success();
+
+	UFUNCTION(BlueprintCallable, Category = "JavascriptNotification")
+	void Pending();
+
+	UFUNCTION(BlueprintCallable, Category = "JavascriptNotification")
+	void Fail();
+
+	UFUNCTION(BlueprintCallable, Category = "JavascriptNotification")
+	void Fadeout();
+
+	UFUNCTION(BlueprintCallable, Category = "JavascriptNotification")
+	void Reset();
+
+	/** The text displayed in this text block */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	FText Text;
+
+	/** Setup information for the buttons on the notification */
+	//TArray<FNotificationButtonInfo> ButtonDetails;
+
+	/** The icon image to display next to the text */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	bool bUseImage;
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	FSlateBrush Image;
+
+	/** The fade in duration for this element */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	float FadeInDuration;
+
+	/** The fade out duration for this element */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	float FadeOutDuration;
+
+	/** The duration before a fadeout for this element */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	float ExpireDuration;
+
+	/** Controls whether or not to add the animated throbber */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	bool bUseThrobber;
+
+	/** Controls whether or not to display the success and fail icons */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	bool bUseSuccessFailIcons;
+
+	/** When true the larger bolder font will be used to display the message */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	bool bUseLargeFont;
+
+	/** When set this forces the width of the box, used to stop resizing on text change */
+	//FOptionalSize WidthOverride;
+
+	/** When true the notification will automatically time out after the expire duration. */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	bool bFireAndForget;
+
+	/** When set this will display a check box on the notification; handles getting the current check box state */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	TEnumAsByte<ECheckBoxState> CheckBoxState;
+
+	/** When set this will display a check box on the notification; handles setting the new check box state */
+	UPROPERTY()
+	FCheckDelegate CheckBoxStateChanged;
+
+	/** Text to display for the check box message */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	FText CheckBoxText;
+
+	/** When set this will display as a hyperlink on the right side of the notification. */
+	UPROPERTY()
+	FSimpleDelegate Hyperlink;
+
+	/** Text to display for the hyperlink message */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	FText HyperlinkText;
+
+	/** True if we should throttle the editor while the notification is transitioning and performance is poor, to make sure the user can see the animation */
+	UPROPERTY(BlueprintReadWrite, Category = "Javscript|Editor")
+	bool bAllowThrottleWhenFrameRateIsLow;
+
+	ECheckBoxState GetCheckBoxState() const;
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUICommands.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUICommands.cpp
new file mode 100644
index 0000000..8e4ba16
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUICommands.cpp
@@ -0,0 +1,136 @@
+#include "JavascriptEditor.h"
+#include "JavascriptUICommands.h"
+#include "Framework/Commands/Commands.h"
+
+PRAGMA_DISABLE_OPTIMIZATION
+
+UJavascriptUICommands::UJavascriptUICommands(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer), bRegistered(false)
+{	
+}
+
+#if WITH_EDITOR
+void UJavascriptUICommands::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	Discard();
+}
+#endif
+
+void UJavascriptUICommands::Commit()
+{
+#if WITH_EDITOR
+	Discard();
+
+	IJavascriptEditorModule::Get().AddExtension(this);
+	
+	bRegistered = true;
+#endif
+}
+
+void UJavascriptUICommands::Discard()
+{
+#if WITH_EDITOR
+	if (bRegistered)
+	{
+		IJavascriptEditorModule::Get().RemoveExtension(this);
+	}
+
+	bRegistered = false;
+#endif
+}
+
+#if WITH_EDITOR
+void UJavascriptUICommands::Register()
+{	
+	BindingContext = MakeShareable(new FBindingContext(*ContextName, ContextDesc, ContextNameParent, StyleSetName.IsNone() ? FEditorStyle::GetStyleSetName() : StyleSetName));
+
+	for (auto info : Commands)
+	{
+		TSharedPtr<FUICommandInfo> CommandInfo;
+
+		if (info.FriendlyName.Len() == 0)
+		{
+			info.FriendlyName = info.Id;
+		}
+
+		UI_COMMAND_Function(
+			BindingContext.Get(), 
+			CommandInfo, 
+			TEXT(""), 
+			*info.Id, 
+			*FString::Printf(TEXT("%s_Tooltip"), *info.Id), 
+			TCHAR_TO_ANSI(*FString::Printf(TEXT(".%s"), *info.Id)), 
+			*info.FriendlyName, 
+			*info.Description, 
+			EUserInterfaceActionType::Type(info.ActionType.GetValue()), 
+			info.DefaultChord);
+
+		CommandInfos.Add(CommandInfo);
+	}
+	
+	FBindingContext::CommandsChanged.Broadcast();
+}
+
+void UJavascriptUICommands::Unregister()
+{
+	FInputBindingManager::Get().RemoveContextByName(BindingContext->GetContextName());
+
+	CommandInfos.Empty();
+	
+	FBindingContext::CommandsChanged.Broadcast();
+}
+#endif
+
+void UJavascriptUICommands::Refresh()
+{
+}
+
+#if WITH_EDITOR
+void UJavascriptUICommands::Bind(FUICommandList* CommandList)
+{
+	for (int32 Index = 0; Index < Commands.Num(); ++Index)
+	{
+		CommandList->MapAction(
+			CommandInfos[Index],
+			FExecuteAction::CreateLambda([this, Index](){
+				if (OnExecuteAction.IsBound())
+				{
+					OnExecuteAction.Execute(Commands[Index].Id);
+				}
+			}),
+			FCanExecuteAction::CreateLambda([this, Index](){
+				return OnCanExecuteAction.IsBound() && OnCanExecuteAction.Execute(Commands[Index].Id);
+			}),
+			FCanExecuteAction::CreateLambda([this, Index](){
+				return OnIsActionChecked.IsBound() && OnIsActionChecked.Execute(Commands[Index].Id);
+			}),
+			FCanExecuteAction::CreateLambda([this, Index](){
+				return OnIsActionButtonVisible.IsBound() && OnIsActionButtonVisible.Execute(Commands[Index].Id);
+			})
+		);
+	}
+	
+}
+void UJavascriptUICommands::Unbind(FUICommandList* CommandList)
+{
+	// unbind is not supported, just rebind again!
+}
+
+TSharedPtr<FUICommandInfo> UJavascriptUICommands::GetAction(FString Id)
+{
+	if (Commands.Num() == CommandInfos.Num())
+	{
+		for (int32 Index = 0; Index < Commands.Num(); ++Index)
+		{
+			if (Commands[Index].Id == Id)
+			{
+				return CommandInfos[Index];
+			}
+		}
+	}
+
+	return TSharedPtr<FUICommandInfo>();
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUICommands.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUICommands.h
new file mode 100644
index 0000000..5eea530
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUICommands.h
@@ -0,0 +1,110 @@
+#pragma once
+
+#include "JavascriptUICommands.generated.h"
+
+UENUM()
+namespace EJavasrciptUserInterfaceActionType
+{
+	enum Type
+	{
+		/** Momentary buttons or menu items.  These support enable state, and execute a delegate when clicked. */
+		Button,
+
+		/** Toggleable buttons or menu items that store on/off state.  These support enable state, and execute a delegate when toggled. */
+		ToggleButton,
+		
+		/** Radio buttons are similar to toggle buttons in that they are for menu items that store on/off state.  However they should be used to indicate that menu items in a group can only be in one state */
+		RadioButton,
+
+		/** Similar to Button but will display a readonly checkbox next to the item. */
+		Check
+	};
+}
+
+USTRUCT(BlueprintType)
+struct FJavascriptUICommand
+{
+	GENERATED_BODY()
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString Id;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString FriendlyName;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString Description;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FInputChord DefaultChord;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TEnumAsByte<EJavasrciptUserInterfaceActionType::Type> ActionType;
+};
+
+/**
+*
+*/
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptUICommands : public UObject, public IEditorExtension
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FJavascriptExecuteAction, FString, Id);
+
+	DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(bool, FJavascriptCanExecuteAction, FString, Id);
+
+	UPROPERTY()
+	FJavascriptExecuteAction OnExecuteAction;	
+
+	UPROPERTY()
+	FJavascriptCanExecuteAction OnCanExecuteAction;	
+
+	UPROPERTY()
+	FJavascriptCanExecuteAction OnIsActionChecked;	
+
+	UPROPERTY()
+	FJavascriptCanExecuteAction OnIsActionButtonVisible;	
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TArray<FJavascriptUICommand> Commands;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FString ContextName;
+	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FText ContextDesc;
+	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FName ContextNameParent;
+	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FName StyleSetName;
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Commit();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Discard();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	void Refresh();
+
+	bool bRegistered;
+
+#if WITH_EDITOR
+	TArray<TSharedPtr<FUICommandInfo>> CommandInfos;
+	TSharedPtr<FBindingContext> BindingContext;
+
+	void Bind(FUICommandList* CommandList);
+	void Unbind(FUICommandList* CommandList);
+
+	virtual void Register() override;
+	virtual void Unregister() override;
+
+	virtual void BeginDestroy() override;
+
+	TSharedPtr<FUICommandInfo> GetAction(FString Id);
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUIExtender.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUIExtender.cpp
new file mode 100644
index 0000000..adc194f
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUIExtender.cpp
@@ -0,0 +1,175 @@
+#include "JavascriptEditor.h"
+
+#if WITH_EDITOR
+#include "JavascriptUIExtender.h"
+#include "JavascriptUICommands.h"
+
+namespace
+{
+	bool SectionDisabled{ false };
+	FMenuBuilder* CurrentMenuBuilder{ nullptr };
+	FToolBarBuilder* CurrentToolbarBuilder{ nullptr };
+}
+#endif
+
+UJavascriptUIExtender::UJavascriptUIExtender(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+void UJavascriptUIExtender::BeginSection(FName Name, FText Text)
+{
+#if WITH_EDITOR
+	if (SectionDisabled) return;
+
+	if (CurrentMenuBuilder)
+	{
+		CurrentMenuBuilder->BeginSection(Name, Text);
+	}
+	else if (CurrentToolbarBuilder)
+	{
+		CurrentToolbarBuilder->BeginSection(Name);
+	}
+#endif
+}
+
+void UJavascriptUIExtender::EndSection()
+{
+#if WITH_EDITOR
+	if (SectionDisabled) return;
+
+	if (CurrentMenuBuilder)
+	{
+		CurrentMenuBuilder->EndSection();
+	}
+	else if (CurrentToolbarBuilder)
+	{
+		CurrentToolbarBuilder->EndSection();
+	}
+#endif
+}
+
+void UJavascriptUIExtender::AddMenuEntry(UJavascriptUICommands* Commands, FString Id)
+{
+#if WITH_EDITOR
+	if (CurrentMenuBuilder)
+	{
+		auto Action = Commands->GetAction(Id);
+		if (Action.IsValid())
+		{
+			CurrentMenuBuilder->AddMenuEntry(Action);
+		}		
+	}
+#endif
+}
+
+void UJavascriptUIExtender::AddMenuSeparator()
+{	
+#if WITH_EDITOR
+	if (CurrentMenuBuilder)
+	{
+		CurrentMenuBuilder->AddMenuSeparator();		
+	}
+#endif
+}
+
+void UJavascriptUIExtender::AddToolBarButton(UJavascriptUICommands* Commands, FString Id)
+{
+#if WITH_EDITOR
+	if (CurrentToolbarBuilder)
+	{
+		auto Action = Commands->GetAction(Id);
+		if (Action.IsValid())
+		{
+			CurrentToolbarBuilder->AddToolBarButton(Action);
+		}
+	}
+#endif
+}
+
+void UJavascriptUIExtender::Bind(UJavascriptUICommands* Commands)
+{
+#if WITH_EDITOR
+	if (CurrentMenuBuilder && Commands)
+	{
+		auto List = CurrentMenuBuilder->GetTopCommandList();
+		if (List.IsValid())
+		{
+			Commands->Bind(const_cast<FUICommandList*>(List.Get()));
+		}		
+	}
+#endif
+}
+
+#if WITH_EDITOR
+TSharedPtr<FExtender> UJavascriptUIExtender::TakeMenuExtender(const TSharedPtr< FUICommandList >& CommandList)
+{
+	TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
+	for (const auto& Extension : MenuExtensions)
+	{
+		auto Hook = Extension.ExtensionHook;
+		auto HookPosition = EExtensionHook::Position(Extension.HookPosition.GetValue());
+		MenuExtender->AddMenuExtension(
+			Hook,
+			HookPosition,
+			CommandList,
+			FMenuExtensionDelegate::CreateLambda([this, Hook](class FMenuBuilder& MenuBuilder){
+				if (OnHook.IsBound())
+				{
+					CurrentMenuBuilder = &MenuBuilder;
+					OnHook.Execute(Hook);
+					CurrentMenuBuilder = nullptr;
+				}
+		}));
+	}
+	return MenuExtender;
+}
+
+void UJavascriptUIExtender::PushMenuBuilder(FMenuBuilder& MenuBuilder)
+{
+	CurrentMenuBuilder = &MenuBuilder;
+}
+void UJavascriptUIExtender::Reset()
+{
+	CurrentMenuBuilder = nullptr;
+}
+
+void UJavascriptUIExtender::BuildMenu(FMenuBuilder& MenuBuilder)
+{
+	if (OnHook.IsBound())
+	{
+		for (const auto& Extension : MenuExtensions)
+		{
+			auto Hook = Extension.ExtensionHook;
+			
+			PushMenuBuilder(MenuBuilder);
+			OnHook.Execute(Hook);
+			Reset();
+		}
+	}
+}
+
+TSharedPtr<FExtender> UJavascriptUIExtender::TakeToolbarExtender(const TSharedPtr< FUICommandList >& CommandList)
+{
+	TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);
+	for (const auto& Extension : ToolbarExtensions)
+	{
+		auto Hook = Extension.ExtensionHook;
+		auto HookPosition = EExtensionHook::Position(Extension.HookPosition.GetValue());
+		MenuExtender->AddToolBarExtension(
+			Hook,
+			HookPosition,
+			CommandList,
+			FToolBarExtensionDelegate::CreateLambda([this, Hook, HookPosition](FToolBarBuilder& ToolbarBuilder){
+			if (OnHook.IsBound())
+			{
+				SectionDisabled = HookPosition == EExtensionHook::Position::First;
+				CurrentToolbarBuilder = &ToolbarBuilder;
+				OnHook.Execute(Hook);
+				CurrentToolbarBuilder = nullptr;
+			}
+		}));
+	}
+	return MenuExtender;
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUIExtender.h b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUIExtender.h
new file mode 100644
index 0000000..8760a5c
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/JavascriptUIExtender.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "JavascriptUIExtender.generated.h"
+
+UENUM()
+namespace EJavascriptExtensionHook
+{
+	enum Type
+	{
+		/** Inserts the extension before the element or section. */
+		Before,
+		/** Inserts the extension after the element or section. */
+		After,
+		/** Sections only. Inserts the extension at the beginning of the section. */
+		First,
+	};
+}
+
+USTRUCT()
+struct FJavascriptMenuExtension
+{
+	GENERATED_BODY()
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	FName ExtensionHook;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TEnumAsByte<EJavascriptExtensionHook::Type> HookPosition;
+};
+
+class UJavascriptUICommands;
+
+
+/**
+*
+*/
+UCLASS()
+class JAVASCRIPTEDITOR_API UJavascriptUIExtender : public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TArray<FJavascriptMenuExtension> MenuExtensions;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript | Editor")
+	TArray<FJavascriptMenuExtension> ToolbarExtensions;
+
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FOnHook,FName,Hook);	
+
+	UPROPERTY()
+	FOnHook OnHook;
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void BeginSection(FName Name, FText Text);	
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void EndSection();
+
+	// Menu
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void AddMenuEntry(UJavascriptUICommands* Commands, FString Id);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void AddMenuSeparator();
+
+	// Toolbar
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void AddToolBarButton(UJavascriptUICommands* Commands, FString Id);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Editor")
+	static void Bind(UJavascriptUICommands* Commands);
+
+#if WITH_EDITOR
+	TSharedPtr<FExtender> TakeMenuExtender( const TSharedPtr<FUICommandList>& CommandList );
+	TSharedPtr<FExtender> TakeToolbarExtender(const TSharedPtr<FUICommandList>& CommandList);
+	
+	static void PushMenuBuilder(FMenuBuilder& MenuBuilder);
+	static void Reset();
+
+	void BuildMenu(FMenuBuilder& MenuBuilder);
+#endif
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/PropertyEditor.cpp b/Plugins/UnrealJS/Source/JavascriptEditor/PropertyEditor.cpp
new file mode 100644
index 0000000..49e7199
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/PropertyEditor.cpp
@@ -0,0 +1,66 @@
+#include "JavascriptEditor.h"
+#include "PropertyEditor.h"
+
+#define LOCTEXT_NAMESPACE "UMG"
+
+UPropertyEditor::UPropertyEditor(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{}
+
+void UPropertyEditor::SetObject(UObject* Object)
+{
+	ObjectToInspect = Object;
+
+#if WITH_EDITOR
+	if (View.IsValid())
+	{
+		View->SetObject(Object);
+	}	
+#endif
+}
+
+#if WITH_EDITOR
+TSharedRef<SWidget> UPropertyEditor::RebuildWidget()
+{
+	if (IsDesignTime())
+	{
+		return BuildDesignTimeWidget(SNew(SBox)
+			.HAlign(HAlign_Center)
+			.VAlign(VAlign_Center)
+			[
+				SNew(STextBlock)
+				.Text(LOCTEXT("PropertyEditor", "PropertyEditor"))
+			]);
+	}
+	else
+	{
+		FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
+		FDetailsViewArgs DetailsViewArgs(/*bUpdateFromSelection=*/ false, /*bLockable=*/ false, /*bAllowSearch=*/ false, /*InNameAreaSettings=*/ FDetailsViewArgs::HideNameArea, /*bHideSelectionTip=*/ true);
+		View = EditModule.CreateDetailView(DetailsViewArgs);
+
+		if (ObjectToInspect.IsValid())
+		{
+			View->SetObject(ObjectToInspect.Get());
+		}
+
+		View->OnFinishedChangingProperties().AddUObject(this, &UPropertyEditor::OnFinishedChangingProperties);
+		
+		return View.ToSharedRef();
+	}
+}
+
+void UPropertyEditor::ReleaseSlateResources(bool bReleaseChildren)
+{
+	Super::ReleaseSlateResources(bReleaseChildren);
+
+	View.Reset();
+}
+
+void UPropertyEditor::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent)
+{
+	if (PropertyChangedEvent.Property != nullptr && OnChange.IsBound())
+	{
+		OnChange.Broadcast(PropertyChangedEvent.Property->GetFName());
+	}
+}
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptEditor/PropertyEditor.h b/Plugins/UnrealJS/Source/JavascriptEditor/PropertyEditor.h
new file mode 100644
index 0000000..6b38417
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptEditor/PropertyEditor.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "Widget.h"
+#include "Editor/PropertyEditor/Public/PropertyEditorModule.h"
+#include "PropertyEditor.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPropertyEditorParameterChanged, FName, ParameterName);
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTEDITOR_API UPropertyEditor : public UWidget
+{
+	GENERATED_UCLASS_BODY()
+
+	UFUNCTION(BlueprintCallable, Category = "PropertyEditor")
+	void SetObject(UObject* Object);
+
+	UPROPERTY(BlueprintAssignable, Category = "PropertyEditor")
+	FPropertyEditorParameterChanged OnChange;
+	
+	FWeakObjectPtr ObjectToInspect;
+
+#if WITH_EDITOR
+public:	
+	virtual void ReleaseSlateResources(bool bReleaseChildren) override;
+
+protected:
+	TSharedPtr<class IDetailsView> View;
+
+protected:
+	// UWidget interface
+	virtual TSharedRef<SWidget> RebuildWidget() override;
+	// End of UWidget interface
+
+	void OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent);
+#endif	
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptHttp/IJavascriptHttpModule.h b/Plugins/UnrealJS/Source/JavascriptHttp/IJavascriptHttpModule.h
new file mode 100644
index 0000000..3caa353
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptHttp/IJavascriptHttpModule.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "ModuleInterface.h"
+
+/**
+* Interface for the JavascriptHttp Module.
+*/
+class IJavascriptHttpModule : public IModuleInterface
+{
+public:
+
+	/**
+	* Singleton-like access to this module's interface.  This is just for convenience!
+	* Beware of calling this during the shutdown phase, though.  Your module might have been unloaded already.
+	*
+	* @return Returns singleton instance, loading the module on demand if needed
+	*/
+	static inline IJavascriptHttpModule& Get()
+	{
+		return FModuleManager::LoadModuleChecked< IJavascriptHttpModule >("JavascriptHttp");
+	}
+
+	/**
+	* Checks to see if this module is loaded and ready.  It is only valid to call Get() if IsAvailable() returns true.
+	*
+	* @return True if the module is loaded and ready to use
+	*/
+	static inline bool IsAvailable()
+	{
+		return FModuleManager::Get().IsModuleLoaded("JavascriptHttp");
+	}
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.Build.cs b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.Build.cs
new file mode 100644
index 0000000..bb62a0c
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.Build.cs
@@ -0,0 +1,20 @@
+using UnrealBuildTool;
+
+public class JavascriptHttp : ModuleRules
+{
+	public JavascriptHttp(TargetInfo Target)
+	{
+        PublicDependencyModuleNames.AddRange(new string[] { 
+            "Core", 
+            "CoreUObject", 
+            "Engine",             
+            "V8",
+            "Http"
+        });
+
+        if (UEBuildConfiguration.bBuildEditor == true)
+        {
+            PrivateDependencyModuleNames.Add("UnrealEd");
+        }
+	}
+}
diff --git a/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.cpp b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.cpp
new file mode 100644
index 0000000..45a6b19
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.cpp
@@ -0,0 +1,24 @@
+#include "JavascriptHttp.h"
+
+class FJavascripHttpModule : public IJavascriptHttpModule
+{
+	// Begin IModuleInterface
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+	// End IModuleInterface
+
+};
+
+
+void FJavascripHttpModule::StartupModule()
+{
+
+}
+
+
+void FJavascripHttpModule::ShutdownModule()
+{
+
+}
+
+IMPLEMENT_MODULE(FJavascripHttpModule, JavascriptHttp);
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.h b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.h
new file mode 100644
index 0000000..d2ec7d2
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttp.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "CoreUObject.h"
+#include "Engine.h"
+
+#include "IHttpRequest.h"
+#include "IHttpResponse.h"
+#include "HttpModule.h"
+
+// Interfaces
+#include "IJavascriptHttpModule.h"
diff --git a/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttpRequest.cpp b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttpRequest.cpp
new file mode 100644
index 0000000..5d4a34e
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttpRequest.cpp
@@ -0,0 +1,177 @@
+#include "JavascriptHttp.h"
+#include "JavascriptHttpRequest.h"
+#include "JavascriptContext.h"
+
+#if WITH_EDITOR
+#include "TickableEditorObject.h"
+typedef FTickableEditorObject FTickableRequest;
+#else
+#include "Tickable.h"
+typedef FTickableGameObject FTickableRequest;
+#endif
+
+struct FHttpProcessor : public FTickableRequest
+{
+public:
+	FHttpProcessor(TSharedPtr<IHttpRequest> InRef)
+		: Ref(InRef)
+	{}
+
+	TSharedPtr<IHttpRequest> Ref;
+
+	virtual void Tick(float DeltaTime) override
+	{
+		Ref->Tick(DeltaTime);
+	}
+
+	virtual bool IsTickable() const override
+	{
+		return true;
+	}
+
+	virtual TStatId GetStatId() const override
+	{
+		RETURN_QUICK_DECLARE_CYCLE_STAT(JavascriptHttpRequest, STATGROUP_Tickables);
+	}	
+};
+
+UJavascriptHttpRequest::UJavascriptHttpRequest(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+	if (!IsTemplate(RF_ClassDefaultObject))
+	{
+		Request = FHttpModule::Get().CreateRequest();
+	}
+}
+
+void UJavascriptHttpRequest::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	if (IsProcessing())
+	{
+		EndProcessing();
+	}
+
+	Request.Reset();
+}
+
+void UJavascriptHttpRequest::BeginProcessing()
+{
+	Processor = new FHttpProcessor(Request);
+}
+
+void UJavascriptHttpRequest::EndProcessing()
+{
+	delete Processor;
+	Processor = nullptr;
+}
+
+FString UJavascriptHttpRequest::GetVerb()
+{
+	return Request->GetVerb();
+}
+
+void UJavascriptHttpRequest::SetVerb(const FString& Verb)
+{
+	Request->SetVerb(Verb);
+}
+
+void UJavascriptHttpRequest::SetURL(const FString& URL)
+{
+	Request->SetURL(URL);
+}
+
+void UJavascriptHttpRequest::SetContentFromMemory()
+{
+	TArray<uint8> Payload;
+	Payload.Append((uint8*)FArrayBufferAccessor::GetData(), FArrayBufferAccessor::GetSize());
+	Request->SetContent(Payload);
+}
+
+void UJavascriptHttpRequest::SetContentAsString(const FString& ContentString)
+{
+	Request->SetContentAsString(ContentString);
+}
+
+void UJavascriptHttpRequest::SetHeader(const FString& HeaderName, const FString& HeaderValue)
+{
+	Request->SetHeader(HeaderName, HeaderValue);
+}
+
+bool UJavascriptHttpRequest::ProcessRequest()
+{
+	if (IsProcessing()) return false;
+
+	Request->OnProcessRequestComplete().BindLambda([&](FHttpRequestPtr, FHttpResponsePtr, bool status){
+		EndProcessing();
+		OnComplete.ExecuteIfBound(status);
+	});
+
+	Request->OnRequestProgress().BindLambda([&](FHttpRequestPtr, int32 sent, int32 recv){
+		EndProcessing();
+		OnProgress.ExecuteIfBound(sent,recv);
+	});
+	
+	if (Request->ProcessRequest())
+	{
+		BeginProcessing();
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+void UJavascriptHttpRequest::CancelRequest()
+{
+	Request->CancelRequest();
+}
+
+EJavascriptHttpRequestStatus::Type UJavascriptHttpRequest::GetStatus()
+{
+	return EJavascriptHttpRequestStatus::Type(Request->GetStatus());
+}
+
+int32 UJavascriptHttpRequest::GetResponseCode()
+{
+	auto res = Request->GetResponse();
+	if (!res.IsValid()) return 0;
+
+	return Request->GetResponse()->GetResponseCode();
+}
+
+FString UJavascriptHttpRequest::GetContentAsString()
+{
+	auto res = Request->GetResponse();
+	if (!res.IsValid()) return TEXT("");
+
+	return Request->GetResponse()->GetContentAsString();
+}
+
+int32 UJavascriptHttpRequest::GetContentLength()
+{
+	auto res = Request->GetResponse();
+	if (!res.IsValid()) return 0;
+
+	return Request->GetResponse()->GetContent().Num();
+}
+
+void UJavascriptHttpRequest::GetContentToMemory()
+{
+	auto res = Request->GetResponse();
+	if (!res.IsValid()) return;
+
+	const auto& Content = Request->GetResponse()->GetContent();
+
+	if (FArrayBufferAccessor::GetSize() >= Content.Num())
+	{
+		FMemory::Memcpy(FArrayBufferAccessor::GetData(), Content.GetData(), Content.Num());
+	}
+}
+
+float UJavascriptHttpRequest::GetElapsedTime()
+{
+	return Request->GetElapsedTime();
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttpRequest.h b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttpRequest.h
new file mode 100644
index 0000000..77aff56
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptHttp/JavascriptHttpRequest.h
@@ -0,0 +1,179 @@
+#pragma once
+
+#include "JavascriptHttpRequest.generated.h"
+
+UENUM()
+namespace EJavascriptHttpRequestStatus
+{
+	/**
+	* Enumerates the current state of an Http request
+	*/
+	enum Type
+	{
+		/** Has not been started via ProcessRequest() */
+		NotStarted,
+		/** Currently being ticked and processed */
+		Processing,
+		/** Finished but failed */
+		Failed,
+		/** Finished and was successful */
+		Succeeded
+	};
+}
+
+struct FHttpProcessor;
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTHTTP_API UJavascriptHttpRequest : public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	TSharedPtr<IHttpRequest> Request;
+
+	/**
+	* Gets the verb (GET, PUT, POST) used by the request.
+	*
+	* @return the verb string
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	FString GetVerb();
+
+	/**
+	* Sets the verb used by the request.
+	* Eg. (GET, PUT, POST)
+	* Should be set before calling ProcessRequest.
+	* If not specified then a GET is assumed.
+	*
+	* @param Verb - verb to use.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void SetVerb(const FString& Verb);
+
+	/**
+	* Sets the URL for the request
+	* Eg. (http://my.domain.com/something.ext?key=value&key2=value).
+	* Must be set before calling ProcessRequest.
+	*
+	* @param URL - URL to use.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void SetURL(const FString& URL);
+
+	/**
+	* Sets the content of the request (optional data).
+	* Usually only set for POST requests.
+	*
+	* @param ContentPayload - payload to set.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void SetContentFromMemory();
+
+	/**
+	* Sets the content of the request as a string encoded as UTF8.
+	*
+	* @param ContentString - payload to set.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void SetContentAsString(const FString& ContentString);
+
+	/**
+	* Sets optional header info.
+	* Content-Length is the only header set for you.
+	* Required headers depends on the request itself.
+	* Eg. "multipart/form-data" needed for a form post
+	*
+	* @param HeaderName - Name of the header (ie, Content-Type)
+	* @param HeaderValue - Value of the header
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void SetHeader(const FString& HeaderName, const FString& HeaderValue);
+
+	/**
+	* Called to begin processing the request.
+	* OnProcessRequestComplete delegate is always called when the request completes or on error if it is bound.
+	* A request can be re-used but not while still being processed.
+	*
+	* @return if the request was successfully started.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	bool ProcessRequest();
+
+	/**
+	* Delegate called when an Http request completes
+	*
+	* @param first parameter - indicates whether or not the request was able to connect successfully
+	*/
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FJavascriptHttpRequestCompleteDelegate, bool, successful);
+
+	/**
+	* Delegate called per tick to update an Http request upload or download size progress
+	*
+	* @param first parameter - the number of bytes sent / uploaded in the request so far.
+	* @param second parameter - the number of bytes received / downloaded in the response so far.
+	*/
+	DECLARE_DYNAMIC_DELEGATE_TwoParams(FJavascriptHttpRequestProgressDelegate, int32, sent, int32, recv);
+
+	UPROPERTY()
+	FJavascriptHttpRequestCompleteDelegate OnComplete;
+
+	UPROPERTY()
+	FJavascriptHttpRequestProgressDelegate OnProgress;	
+
+	/**
+	* Called to cancel a request that is still being processed
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void CancelRequest();
+
+	/**
+	* Get the current status of the request being processed
+	*
+	* @return the current status
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	EJavascriptHttpRequestStatus::Type GetStatus();
+		
+	/**
+	* Gets the response code returned by the requested server.
+	* See EHttpResponseCodes for known response codes
+	*
+	* @return the response code.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	int32 GetResponseCode();
+
+	/**
+	* Returns the payload as a string, assuming the payload is UTF8.
+	*
+	* @return the payload as a string.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	FString GetContentAsString();	
+
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	int32 GetContentLength();
+
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	void GetContentToMemory();
+
+	/**
+	* Gets the time that it took for the server to fully respond to the request.
+	*
+	* @return elapsed time in seconds.
+	*/
+	UFUNCTION(BlueprintCallable, Category = "Online | Http")
+	float GetElapsedTime();
+
+	virtual void BeginDestroy() override;
+
+	FHttpProcessor* Processor{nullptr};
+
+	bool IsProcessing() { return Processor != nullptr; }
+
+	void BeginProcessing();
+	void EndProcessing();
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/IJavascriptUMGModule.h b/Plugins/UnrealJS/Source/JavascriptUMG/IJavascriptUMGModule.h
new file mode 100644
index 0000000..d761517
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/IJavascriptUMGModule.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "ModuleInterface.h"
+
+/**
+* Interface for the JavascriptUMG Module.
+*/
+class IJavascriptUMGModule : public IModuleInterface
+{
+public:
+
+	/**
+	* Singleton-like access to this module's interface.  This is just for convenience!
+	* Beware of calling this during the shutdown phase, though.  Your module might have been unloaded already.
+	*
+	* @return Returns singleton instance, loading the module on demand if needed
+	*/
+	static inline IJavascriptUMGModule& Get()
+	{
+		return FModuleManager::LoadModuleChecked< IJavascriptUMGModule >("JavascriptUMG");
+	}
+
+	/**
+	* Checks to see if this module is loaded and ready.  It is only valid to call Get() if IsAvailable() returns true.
+	*
+	* @return True if the module is loaded and ready to use
+	*/
+	static inline bool IsAvailable()
+	{
+		return FModuleManager::Get().IsModuleLoaded("JavascriptUMG");
+	}
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptListView.cpp b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptListView.cpp
new file mode 100644
index 0000000..40f5480
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptListView.cpp
@@ -0,0 +1,52 @@
+#include "JavascriptUMG.h"
+#include "JavascriptListView.h"
+#include "JavascriptContext.h"
+
+UJavascriptListView::UJavascriptListView(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+TSharedRef<SWidget> UJavascriptListView::RebuildWidget()
+{
+	MyListView = SNew(SListView< UObject* >)
+		.SelectionMode(SelectionMode)
+		.ListItemsSource(&Items)
+		.ItemHeight(ItemHeight)
+		.OnGenerateRow(BIND_UOBJECT_DELEGATE(SListView< UObject* >::FOnGenerateRow, HandleOnGenerateRow))
+		.OnSelectionChanged_Lambda([this](UObject* Object, ESelectInfo::Type SelectInfo){
+			OnSelectionChanged(Object, SelectInfo);
+		})
+		.OnMouseButtonClick_Lambda([this](UObject* Object){
+			OnClick(Object);
+		})
+		.OnMouseButtonDoubleClick_Lambda([this](UObject* Object){
+			OnDoubleClick(Object);
+		})
+		//.OnContextMenuOpening(this, &SSocketManager::OnContextMenuOpening)
+		//.OnItemScrolledIntoView(this, &SSocketManager::OnItemScrolledIntoView)
+		//	.HeaderRow
+		//	(
+		//		SNew(SHeaderRow)
+		//		.Visibility(EVisibility::Collapsed)
+		//		+ SHeaderRow::Column(TEXT("Socket"))
+		//	);
+		;
+
+	return BuildDesignTimeWidget(MyListView.ToSharedRef());
+}
+
+void UJavascriptListView::ProcessEvent(UFunction* Function, void* Parms)
+{
+	if (JavascriptContext && JavascriptContext->CallProxyFunction(this, this, Function, Parms))
+	{
+		return;
+	}
+
+	Super::ProcessEvent(Function, Parms);
+}
+
+void UJavascriptListView::RequestListRefresh()
+{
+	MyListView->RequestListRefresh();
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptListView.h b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptListView.h
new file mode 100644
index 0000000..d8b7feb
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptListView.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "ListView.h"
+#include "JavascriptListView.generated.h"
+
+class UJavascriptContext;
+
+/**
+* Allows thousands of items to be displayed in a list.  Generates widgets dynamically for each item.
+*/
+UCLASS(Experimental)
+class JAVASCRIPTUMG_API UJavascriptListView : public UListView
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript")
+	UJavascriptContext* JavascriptContext;
+
+	/** Event fired when a tutorial stage ends */
+	UFUNCTION(BlueprintImplementableEvent, Category = "Javascript")
+	void OnClick(UObject* Object);
+
+	/** Event fired when a tutorial stage ends */
+	UFUNCTION(BlueprintImplementableEvent, Category = "Javascript")
+	void OnDoubleClick(UObject* Object);
+
+	/** Event fired when a tutorial stage ends */
+	UFUNCTION(BlueprintImplementableEvent, Category = "Javascript")
+	void OnSelectionChanged(UObject* Object,ESelectInfo::Type Type);	
+
+	/** Refreshes the list */
+	UFUNCTION(BlueprintCallable, Category = "Behavior")
+	void RequestListRefresh();
+
+	// UWidget interface
+	virtual TSharedRef<SWidget> RebuildWidget() override;
+	// End of UWidget interface
+
+	// UObject interface
+	virtual void ProcessEvent(UFunction* Function, void* Parms) override;
+	// End of UObject interface
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptTileView.cpp b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptTileView.cpp
new file mode 100644
index 0000000..2c134e8
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptTileView.cpp
@@ -0,0 +1,44 @@
+#include "JavascriptUMG.h"
+#include "JavascriptTileView.h"
+#include "JavascriptContext.h"
+
+UJavascriptTileView::UJavascriptTileView(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+TSharedRef<SWidget> UJavascriptTileView::RebuildWidget()
+{
+	MyTileView = SNew(STileView< UObject* >)
+		.SelectionMode(SelectionMode)
+		.ListItemsSource(&Items)
+		.ItemHeight(ItemHeight)
+		.OnGenerateTile(BIND_UOBJECT_DELEGATE(STileView< UObject* >::FOnGenerateRow, HandleOnGenerateTile))
+		.OnSelectionChanged_Lambda([this](UObject* Object, ESelectInfo::Type SelectInfo){
+			OnSelectionChanged(Object, SelectInfo);
+		})
+		.OnMouseButtonDoubleClick_Lambda([this](UObject* Object){
+			OnDoubleClick(Object);
+		})
+		//.OnContextMenuOpening(this, &SSocketManager::OnContextMenuOpening)
+		//.OnItemScrolledIntoView(this, &SSocketManager::OnItemScrolledIntoView)
+		//	.HeaderRow
+		//	(
+		//		SNew(SHeaderRow)
+		//		.Visibility(EVisibility::Collapsed)
+		//		+ SHeaderRow::Column(TEXT("Socket"))
+		//	);
+		;
+
+	return BuildDesignTimeWidget(MyTileView.ToSharedRef());
+}
+
+void UJavascriptTileView::ProcessEvent(UFunction* Function, void* Parms)
+{
+	if (JavascriptContext && JavascriptContext->CallProxyFunction(this, this, Function, Parms))
+	{
+		return;
+	}
+
+	Super::ProcessEvent(Function, Parms);
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptTileView.h b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptTileView.h
new file mode 100644
index 0000000..b9cd8d4
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptTileView.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "TileView.h"
+#include "JavascriptTileView.generated.h"
+
+class UJavascriptContext;
+
+/**
+* Allows thousands of items to be displayed in a Tile.  Generates widgets dynamically for each item.
+*/
+UCLASS(Experimental)
+class JAVASCRIPTUMG_API UJavascriptTileView : public UTileView
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript")
+	UJavascriptContext* JavascriptContext;
+
+	/** Event fired when a tutorial stage ends */
+	UFUNCTION(BlueprintImplementableEvent, Category = "Javascript")
+	void OnClick(UObject* Object);
+
+	/** Event fired when a tutorial stage ends */
+	UFUNCTION(BlueprintImplementableEvent, Category = "Javascript")
+	void OnDoubleClick(UObject* Object);
+
+	/** Event fired when a tutorial stage ends */
+	UFUNCTION(BlueprintImplementableEvent, Category = "Javascript")
+	void OnSelectionChanged(UObject* Object,ESelectInfo::Type Type);	
+
+	// UWidget interface
+	virtual TSharedRef<SWidget> RebuildWidget() override;
+	// End of UWidget interface
+
+	// UObject interface
+	virtual void ProcessEvent(UFunction* Function, void* Parms) override;
+	// End of UObject interface
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.Build.cs b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.Build.cs
new file mode 100644
index 0000000..5880e48
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.Build.cs
@@ -0,0 +1,21 @@
+using UnrealBuildTool;
+
+public class JavascriptUMG : ModuleRules
+{
+	public JavascriptUMG(TargetInfo Target)
+	{
+        PublicDependencyModuleNames.AddRange(new string[] { 
+            "Core", 
+            "CoreUObject", 
+            "Engine", 
+            "InputCore", 
+            "Slate",
+			"SlateCore",
+            "ShaderCore",
+			"RenderCore",
+			"RHI", 
+            "UMG",
+            "V8"
+        });        
+	}
+}
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.cpp b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.cpp
new file mode 100644
index 0000000..876c271
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.cpp
@@ -0,0 +1,24 @@
+#include "JavascriptUMG.h"
+
+class FJavascripUMGModule : public IJavascriptUMGModule
+{
+	// Begin IModuleInterface
+	virtual void StartupModule() override;
+	virtual void ShutdownModule() override;
+	// End IModuleInterface
+
+};
+
+
+void FJavascripUMGModule::StartupModule()
+{
+
+}
+
+
+void FJavascripUMGModule::ShutdownModule()
+{
+
+}
+
+IMPLEMENT_MODULE(FJavascripUMGModule, JavascriptUMG);
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.h b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.h
new file mode 100644
index 0000000..3debfc0
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMG.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "Engine.h"
+
+// Interfaces
+#include "IJavascriptUMGModule.h"
+
+#include "UMG.h"
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGBlueprintLibrary.cpp b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGBlueprintLibrary.cpp
new file mode 100644
index 0000000..0c2b312
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGBlueprintLibrary.cpp
@@ -0,0 +1,12 @@
+#include "JavascriptUMG.h"
+#include "JavascriptUMGBlueprintLibrary.h"
+
+FSlateColor UJavascriptUMGBlueprintLibrary::SlateColor_UseForeground()
+{
+	return FSlateColor::UseForeground();
+}
+
+FSlateColor UJavascriptUMGBlueprintLibrary::SlateColor_UseSubduedForeground()
+{
+	return FSlateColor::UseSubduedForeground();
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGBlueprintLibrary.h b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGBlueprintLibrary.h
new file mode 100644
index 0000000..553bf1d
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGBlueprintLibrary.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "JavascriptUMGBlueprintLibrary.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTUMG_API UJavascriptUMGBlueprintLibrary : public UUserWidget
+{
+	GENERATED_BODY()
+
+public:	
+	UFUNCTION(BlueprintCallable, Category = "Scripting | Javascript")
+	static FSlateColor SlateColor_UseForeground();	
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting | Javascript")
+	static FSlateColor SlateColor_UseSubduedForeground();
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGLibrary.cpp b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGLibrary.cpp
new file mode 100644
index 0000000..7c34a98
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGLibrary.cpp
@@ -0,0 +1,2 @@
+#include "JavascriptUMG.h"
+#include "JavascriptUMGLibrary.h"
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGLibrary.h b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGLibrary.h
new file mode 100644
index 0000000..52b53cf
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptUMGLibrary.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "JavascriptUMGLibrary.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTUMG_API UJavascriptUMGLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_BODY()
+};
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptWidget.cpp b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptWidget.cpp
new file mode 100644
index 0000000..1b45cbe
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptWidget.cpp
@@ -0,0 +1,47 @@
+#include "JavascriptUMG.h"
+#include "JavascriptWidget.h"
+#include "JavascriptContext.h"
+#include "Blueprint/WidgetTree.h"
+
+UJavascriptWidget::UJavascriptWidget(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+	WidgetTree = CreateDefaultSubobject<UWidgetTree>(TEXT("WidgetTree"));
+	WidgetTree->SetFlags(RF_Transactional);
+}
+
+void UJavascriptWidget::SetRootWidget(UWidget* Widget)
+{
+	WidgetTree->RootWidget = Widget;
+}
+
+void UJavascriptWidget::ProcessEvent(UFunction* Function, void* Parms)
+{
+	if (JavascriptContext && JavascriptContext->CallProxyFunction(this, this, Function, Parms))
+	{		
+		return;
+	}
+	
+	Super::ProcessEvent(Function, Parms);	
+}
+
+void UJavascriptWidget::CallSynchronizeProperties(UVisual* Visual)
+{
+	if (auto Widget = Cast<UWidget>(Visual))
+	{
+		TSharedPtr<SWidget> SafeWidget = Widget->GetCachedWidget();
+		if (SafeWidget.IsValid())
+		{
+			Widget->SynchronizeProperties();
+		}
+	}
+	else if (auto Slot = Cast<UPanelSlot>(Visual))
+	{
+		Slot->SynchronizeProperties();
+	}	
+}
+
+bool UJavascriptWidget::HasValidCachedWidget(UWidget* Widget)
+{
+	return Widget && Widget->GetCachedWidget().IsValid();
+}
diff --git a/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptWidget.h b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptWidget.h
new file mode 100644
index 0000000..4aa19c8
--- /dev/null
+++ b/Plugins/UnrealJS/Source/JavascriptUMG/JavascriptWidget.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "UserWidget.h"
+#include "JavascriptWidget.generated.h"
+
+class UJavascriptWidget;
+class UJavascriptContext;
+
+/**
+ * 
+ */
+UCLASS()
+class JAVASCRIPTUMG_API UJavascriptWidget : public UUserWidget
+{
+	GENERATED_UCLASS_BODY()
+
+public:	
+	UPROPERTY(BlueprintReadWrite, Category = "Javascript")
+	UJavascriptContext* JavascriptContext;
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting | Javascript")
+	void SetRootWidget(UWidget* Widget);	
+
+	virtual void ProcessEvent(UFunction* Function, void* Parms) override;
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting | Javascript")
+	static void CallSynchronizeProperties(UVisual* WidgetOrSlot);	
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting | Javascript")
+	static bool HasValidCachedWidget(UWidget* Widget);	
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Config.h b/Plugins/UnrealJS/Source/V8/Private/Config.h
new file mode 100644
index 0000000..ea2f063
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Config.h
@@ -0,0 +1,150 @@
+#pragma once
+
+struct FV8Config
+{
+	static FString Safeify(const FString& Name)
+	{
+		if (Name == "Object")
+		{
+			return TEXT("UObject");
+		}
+		else if (Name == "Node")
+		{
+			return TEXT("UNode");
+		}
+		else if (Name == "Function")
+		{
+			return TEXT("UFunction");
+		}
+		else if (Name == "PointerEvent")
+		{
+			return TEXT("UPointerEvent");
+		}
+		else if (Name == "Image")
+		{
+			return TEXT("UImage");
+		}
+		else if (Name == "Selection")
+		{
+			return TEXT("USelection");
+		}
+		else if (Name == "FocusEvent")
+		{
+			return TEXT("UFocusEvent");
+		}
+		else
+		{
+			return Name.Replace(TEXT(" "),TEXT(""));
+		}
+	}
+
+	// 
+	static bool CanExportClass(const UClass* Class)
+	{
+		bool bCanExport = (Class->ClassFlags & (CLASS_RequiredAPI | CLASS_MinimalAPI)) != 0;
+
+		return bCanExport;
+	}
+
+	// 
+	static bool CanExportFunction(const UClass* Class, const UFunction* Function)
+	{		
+		// Delegate function is not a real function.
+		if ((Function->FunctionFlags & FUNC_Delegate))
+		{
+			return false;
+		}
+
+		// Skip unsupported type or delegate properties which are handled in dedicated code path.
+		for (TFieldIterator<UProperty> ParamIt(Function); ParamIt; ++ParamIt)
+		{
+			UProperty* Param = *ParamIt;
+			if (Param->ArrayDim > 1 ||
+				Param->IsA(UDelegateProperty::StaticClass()) ||
+				Param->IsA(UMulticastDelegateProperty::StaticClass()) ||
+				Param->IsA(UInterfaceProperty::StaticClass()))
+			{
+				return false;
+			}
+		}
+
+		return true;
+	}
+		
+	static bool CanExportProperty(const UStruct* Class, const UProperty* Property)
+	{
+		// Skip unsupported static array and interface.
+		if (Property->ArrayDim > 1 ||			
+			Property->IsA(UInterfaceProperty::StaticClass()))
+		{
+			return false;
+		}
+
+		return true;
+	}
+
+	static bool IsWriteDisabledProperty(UProperty* PropertyToExport)
+	{
+		return false;		
+	}
+
+	static FString GetAlias(UFunction* Function, bool no_empty = false)
+	{
+		auto has_meta = [](UField* This, const FName& Key) {
+			UPackage* Package = This->GetOutermost();
+			check(Package);
+
+			UMetaData* MetaData = Package->GetMetaData();
+			check(MetaData);
+
+			bool bHasMetaData = MetaData->HasValue(This, Key);
+
+			return bHasMetaData;
+		};
+
+		auto get_meta = [](UField* This, const FName& Key) {
+			UPackage* Package = This->GetOutermost();
+			check(Package);
+
+			UMetaData* MetaData = Package->GetMetaData();
+			check(MetaData);
+
+			const FString& MetaDataString = MetaData->GetValue(This, Key);
+
+			return MetaDataString;
+		};
+
+		static FName NAME_DisplayName("DisplayName");
+		if (has_meta(Function, NAME_DisplayName))
+		{
+			FString Name = get_meta(Function, NAME_DisplayName).Replace(TEXT(" "), TEXT(""));
+
+			int32 Index;
+			if (Name.FindChar('(', Index))
+			{
+				Name = Name.Mid(0, Index);
+			}
+
+			if (Name.Len() > 0)
+			{
+				bool pass = true;
+				for (auto Ch : Name)
+				{
+					if (!FChar::IsIdentifier(Ch))
+					{
+						pass = false;
+						break;
+					}
+				}
+
+				if (pass && Name != Function->GetName())
+				{
+					return Name;
+				}
+			}
+		}
+
+		return no_empty ? Safeify(Function->GetName()) : TEXT("");
+	}
+};
+
diff --git a/Plugins/UnrealJS/Source/V8/Private/Debugger.cpp b/Plugins/UnrealJS/Source/V8/Private/Debugger.cpp
new file mode 100644
index 0000000..d1e1944
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Debugger.cpp
@@ -0,0 +1,367 @@
+#include "V8PCH.h"
+#include "JavascriptIsolate.h"
+#include "JavascriptContext.h"
+#include "SocketSubSystem.h"
+#include "Sockets.h"
+#include "NetworkMessage.h"
+#include "Misc/DefaultValueHelper.h"
+
+#if PLATFORM_WINDOWS
+#include "AllowWindowsPlatformTypes.h"
+#endif
+#include <thread>
+#if PLATFORM_WINDOWS
+#include "HideWindowsPlatformTypes.h"
+#endif
+
+#include "Translator.h"
+
+using namespace v8;
+
+#if WITH_EDITOR
+#include "TickableEditorObject.h"
+
+namespace 
+{
+	//@HACK : to be multi-threaded and support multiple sockets
+	FSocket* MainSocket{ nullptr };
+
+	void RegisterDebuggerSocket(FSocket* Socket)
+	{
+		MainSocket = Socket;
+	}
+
+	void UnregisterDebuggerSocket(FSocket* Socket)
+	{
+		MainSocket = nullptr;
+	}
+
+	void SendTo(FSocket* Socket_, const char* msg)
+	{
+		FSimpleAbstractSocket_FSocket Socket(Socket_);
+
+		auto send = [&](const char* msg) {
+			Socket.Send(reinterpret_cast<const uint8*>(msg), strlen(msg));
+		};
+
+		send(TCHAR_TO_UTF8(*FString::Printf(TEXT("Content-Length: %d\r\n"), strlen(msg))));
+		send("\r\n");
+		send(msg);
+
+		//UE_LOG(Javascript, Log, TEXT("Reply : %s"), UTF8_TO_TCHAR(msg));
+	}
+
+	void Broadcast(const char* msg)
+	{
+		if (MainSocket)
+		{
+			SendTo(MainSocket,msg);
+		}
+
+		//UE_LOG(Javascript, Log, TEXT("Broadcast : %s"), UTF8_TO_TCHAR(msg));
+	}
+}
+
+class FDebugger : public IJavascriptDebugger, public FTickableEditorObject
+{
+public:
+	struct FClientData : public Debug::ClientData
+	{
+		FClientData(FDebugger* InDebugger, FSocket* InSocket)
+		: Debugger(InDebugger), Socket_(InSocket)
+		{
+			Debugger->Busy.Increment();
+		}
+
+		~FClientData()
+		{
+			Debugger->Busy.Decrement();
+		}
+
+		FDebugger* Debugger;
+		FSocket* Socket_;
+
+		void Send(const char* msg)
+		{
+			SendTo(Socket_, msg);
+		}
+	};
+
+	std::thread thread;
+	Isolate* isolate_;
+	Persistent<Context> context_;
+
+	Local<Context> context() { return Local<Context>::New(isolate_, context_); }
+
+	virtual void Tick(float DeltaTime) override
+	{
+		Isolate::Scope isolate_scope(isolate_);
+		HandleScope handle_scope(isolate_);
+		Context::Scope debug_scope(context());
+
+		Debug::ProcessDebugMessages();
+	}
+
+	virtual bool IsTickable() const override
+	{
+		return true;
+	}
+
+	virtual TStatId GetStatId() const override
+	{
+		RETURN_QUICK_DECLARE_CYCLE_STAT(FV8Debugger, STATGROUP_Tickables);
+	}	
+
+	FDebugger(int32 InPort, Local<Context> context)
+	{
+		isolate_ = context->GetIsolate();
+		context_.Reset(isolate_,context);
+
+		StopRequested.Reset();
+
+		Install();
+
+		thread = std::thread([this, InPort]{Main(InPort); });				
+	}
+
+	void Install()
+	{
+		Debug::SetMessageHandler([](const Debug::Message& message){
+			FClientData* data = static_cast<FClientData*>(message.GetClientData());
+			auto json = message.GetJSON();
+
+			//UE_LOG(Javascript, Log, TEXT("V8 message : %s %s"), message.IsEvent() ? TEXT("(event)") : TEXT(""), message.IsResponse() ? TEXT("(response)") : TEXT("") );			
+
+			if (json.IsEmpty())
+			{
+				UE_LOG(Javascript, Error, TEXT("Not a json"));
+			}
+			else
+			{
+				String::Utf8Value utf8(json);
+				auto str = *utf8;
+
+				if (data)
+				{
+					data->Send(str);
+				}
+				else
+				{
+					Broadcast(str);
+				}
+			}
+		});
+	}
+
+	void Uninstall()
+	{		
+		Context::Scope context_scope(context());
+		Debug::SetMessageHandler(nullptr);
+	}
+
+	~FDebugger()
+	{
+		Uninstall();
+
+		context_.Reset();
+	}
+
+	virtual void Destroy() override
+	{
+		Stop();		
+
+		delete this;
+	}
+
+	void Stop()
+	{
+		StopRequested.Set(true);
+
+		thread.join();
+	}	
+	
+	bool Main(int32 Port)
+	{
+		auto SocketSubsystem = ISocketSubsystem::Get();
+		if (!SocketSubsystem) return ReportError("No socket subsystem");
+
+		auto Socket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("V8 debugger"));
+		if (!Socket) return ReportError("Failed to create socket");
+
+		auto Cleanup_Socket = [&]{Socket->Close(); SocketSubsystem->DestroySocket(Socket); };
+		
+		// listen on any IP address
+		auto ListenAddr = SocketSubsystem->GetLocalBindAddr(*GLog);
+
+		ListenAddr->SetPort(Port);
+		Socket->SetReuseAddr();
+
+		// bind to the address
+		if (!Socket->Bind(*ListenAddr)) return ReportError("Failed to bind socket", Cleanup_Socket );
+
+		if (!Socket->Listen(16)) return ReportError("Failed to listen", Cleanup_Socket );
+				
+		int32 port = Socket->GetPortNo();
+		check((Port == 0 && port != 0) || port == Port);
+
+		ListenAddr->SetPort(port);		
+
+		while (!StopRequested.GetValue())
+		{
+			bool bReadReady = false;
+
+			// check for incoming connections
+			if (Socket->HasPendingConnection(bReadReady) && bReadReady)
+			{
+				auto ClientSocket = Socket->Accept(TEXT("Remote V8 connection"));
+
+				if (!ClientSocket) continue;				
+
+				RegisterDebuggerSocket(ClientSocket);
+
+				UE_LOG(Javascript, Log, TEXT("V8 Debugger session start"));
+
+				Logic(ClientSocket);
+
+				UE_LOG(Javascript, Log, TEXT("V8 Debugger session stop"));
+
+				while (Busy.GetValue() != 0)
+				{
+					FPlatformProcess::Sleep(0.25f);
+				}
+
+				UnregisterDebuggerSocket(ClientSocket);
+				
+				ClientSocket->Close();
+				SocketSubsystem->DestroySocket(ClientSocket);
+			}
+			else
+			{
+				FPlatformProcess::Sleep(0.25f);
+			}		
+		}
+
+		Cleanup_Socket();		
+
+		return true;
+	}
+
+	bool Logic(FSocket* InSocket)
+	{
+		FSimpleAbstractSocket_FSocket Socket(InSocket);
+
+		auto send = [&](const char* msg) {
+			Socket.Send(reinterpret_cast<const uint8*>(msg), strlen(msg));
+		};
+
+		auto read_line = [&](FString& line) {
+			const auto buffer_size = 1024;
+			ANSICHAR buffer[buffer_size];
+			for (int pos = 0; pos < buffer_size; pos++)			
+			{
+				char ch;
+				if (!Socket.Receive(reinterpret_cast<uint8*>(&ch), 1)) return false;
+
+				if (pos && buffer[pos - 1] == '\r' && ch == '\n')
+				{
+					buffer[pos - 1] = '\0';
+					line = UTF8_TO_TCHAR(buffer);
+					return true;
+				}
+				else if (ch == '\n')
+				{
+					buffer[pos] = '\0';
+					line = UTF8_TO_TCHAR(buffer);
+					return true;
+				}
+				buffer[pos] = ch;				
+			}
+			return false;
+		};
+
+		auto command = [&](const FString& request) {			
+			Debug::SendCommand(isolate_, (uint16_t*)*request, request.Len(), new FClientData(this,InSocket));
+		};
+
+		auto process = [&](const FString& content) {
+			//UE_LOG(Javascript, Log, TEXT("Received: %s"), *content);
+			command(content);
+			return content.Find(TEXT("\"type\":\"request\",\"command\":\"disconnect\"}")) == INDEX_NONE;
+		};
+
+		auto bye = [&] {
+			command(TEXT("{\"seq\":1,\"type:\":\"request\",\"command\":\"disconnect\"}"));
+		};
+
+		send("Type: connect\r\n");
+		send("V8-Version: ");
+		send(v8::V8::GetVersion());
+		send("\r\n");
+		send("Protocol-Version: 1\r\n");
+		send("Embedding-Host: UnrealEngine\r\n");
+		send("Content-Length: 0\r\n");
+		send("\r\n");		
+
+		FString json_text;
+		int32 content_length = -1;
+
+		for (;;)
+		{
+			FString line, left, right;
+			if (!read_line(line)) break;			
+
+			if (line.Split(TEXT(":"), &left, &right))
+			{				
+				if (left == TEXT("Content-Length"))
+				{
+					if (!FDefaultValueHelper::ParseInt(right, content_length)) return ReportError("invalid content length");
+				}
+			}
+			else
+			{	
+				if (content_length <= 0) return ReportError("invalid content length < 0");
+
+				char* buffer = new char[content_length+1];
+				if (!buffer) break;
+
+				if (!Socket.Receive(reinterpret_cast<uint8*>(buffer), content_length)) break;
+
+				buffer[content_length] = '\0';
+				if (!process(UTF8_TO_TCHAR(buffer))) return ReportError("failed to process");				
+			}			
+		}
+
+		bye();
+
+		return true;
+	}	
+
+	bool ReportError(const char* Error)
+	{
+		UE_LOG(Javascript, Error, TEXT("%s"), UTF8_TO_TCHAR(Error));
+
+		// always false
+		return false;
+	}
+
+	template <typename Fn>
+	bool ReportError(const char* Error, Fn&& fn)
+	{		
+		fn();
+		return ReportError(Error);
+	}
+
+	FThreadSafeCounter StopRequested;	
+	FThreadSafeCounter Busy;
+};
+
+IJavascriptDebugger* IJavascriptDebugger::Create(int32 InPort,Local<Context> InContext)
+{
+	return new FDebugger(InPort,InContext);
+}
+#else
+IJavascriptDebugger* IJavascriptDebugger::Create(int32 InPort, Local<Context> InContext)
+{
+	return nullptr;
+} 
+#endif
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Delegates.cpp b/Plugins/UnrealJS/Source/V8/Private/Delegates.cpp
new file mode 100644
index 0000000..baa24c3
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Delegates.cpp
@@ -0,0 +1,396 @@
+#include "V8PCH.h"
+#include "JavascriptDelegate.h"
+#include "Translator.h"
+#include "Delegates.h"
+
+using namespace v8;
+
+class FJavascriptDelegate
+{
+public:
+	FWeakObjectPtr WeakObject;
+	UProperty* Property;
+	Persistent<Context> context_;
+	TMap<int32, UniquePersistent<Function>> functions;
+	Persistent<Object> WrappedObject;
+	Isolate* isolate_;
+	int32 NextUniqueId{ 0 };
+	bool bAbandoned{ false };
+
+	bool IsValid() const
+	{
+		return WeakObject.IsValid();
+	}
+
+	FJavascriptDelegate(UObject* InObject, UProperty* InProperty)
+		: WeakObject(InObject), Property(InProperty)
+	{}
+
+	~FJavascriptDelegate()
+	{
+		Purge();
+	}
+
+	void Purge()
+	{
+		if (!bAbandoned)
+		{
+			bAbandoned = true;
+
+			WrappedObject.Reset();
+
+			ClearDelegateObjects();
+
+			context_.Reset();
+		}
+	}
+
+	Local<Object> Initialize(Local<Context> context)
+	{
+		isolate_ = context->GetIsolate();
+		context_.Reset(isolate_, context);
+
+		auto out = Object::New(isolate_);
+
+		auto add = [](const FunctionCallbackInfo<Value>& info) {
+			for (;;)
+			{
+				auto payload = reinterpret_cast<FJavascriptDelegate*>(Local<External>::Cast(info.Data())->Value());
+				if (info.Length() == 1)
+				{
+					auto func = Local<Function>::Cast(info[0]);
+					if (!func.IsEmpty())
+					{
+						payload->Add(func);
+						break;
+					}
+				}
+
+				UE_LOG(Javascript, Log, TEXT("Invalid argument for delegate"));
+				break;
+			}
+		};
+
+		auto remove = [](const FunctionCallbackInfo<Value>& info) {
+			for (;;)
+			{
+				auto payload = reinterpret_cast<FJavascriptDelegate*>(Local<External>::Cast(info.Data())->Value());
+				if (info.Length() == 1)
+				{
+					auto func = Local<Function>::Cast(info[0]);
+					if (!func.IsEmpty())
+					{
+						payload->Remove(func);
+						break;
+					}
+				}
+
+				UE_LOG(Javascript, Log, TEXT("Invalid argument for delegate"));
+				break;
+			}
+		};
+
+		auto clear = [](const FunctionCallbackInfo<Value>& info) {
+			auto payload = reinterpret_cast<FJavascriptDelegate*>(Local<External>::Cast(info.Data())->Value());
+			payload->Clear();			
+		};
+
+		auto toJSON = [](const FunctionCallbackInfo<Value>& info) {
+			auto payload = reinterpret_cast<FJavascriptDelegate*>(Local<External>::Cast(info.Data())->Value());
+
+			uint32_t Index = 0;			
+			auto arr = Array::New(info.GetIsolate(), payload->DelegateObjects.Num());
+			const bool bIsMulticastDelegate = payload->Property->IsA(UMulticastDelegateProperty::StaticClass());
+
+			for (auto DelegateObject : payload->DelegateObjects)
+			{
+				auto JavascriptFunction = payload->functions.Find(DelegateObject->UniqueId);
+				if (JavascriptFunction)
+				{
+					auto function = Local<Function>::New(info.GetIsolate(), *JavascriptFunction);
+					if (!bIsMulticastDelegate)
+					{
+						info.GetReturnValue().Set(function);
+						return;
+					}
+					
+					arr->Set(Index++, function);
+				}
+			}
+
+			if (!bIsMulticastDelegate)
+			{
+				info.GetReturnValue().Set(Null(info.GetIsolate()));
+			}
+			else
+			{
+				info.GetReturnValue().Set(arr);
+			}			
+		};
+
+		auto data = External::New(isolate_, this);
+
+		out->Set(V8_KeywordString(isolate_, "Add"), Function::New(isolate_, add, data));
+		out->Set(V8_KeywordString(isolate_, "Remove"), Function::New(isolate_, remove, data));
+		out->Set(V8_KeywordString(isolate_, "Clear"), Function::New(isolate_, clear, data));
+		out->Set(V8_KeywordString(isolate_, "toJSON"), Function::New(isolate_, toJSON, data));
+
+		WrappedObject.Reset(isolate_, out);
+
+		return out;
+	}
+
+	TArray<UJavascriptDelegate*> DelegateObjects;
+
+	void ClearDelegateObjects()
+	{
+		for (auto obj : DelegateObjects)
+		{
+			obj->RemoveFromRoot();
+		}
+		DelegateObjects.Empty();
+		functions.Empty();
+	}
+
+	void Add(Local<Function> function)
+	{
+		auto DelegateObject = NewObject<UJavascriptDelegate>();
+
+		DelegateObject->UniqueId = NextUniqueId++;
+
+		Bind(DelegateObject, function);
+	}
+
+	UJavascriptDelegate* FindJavascriptDelegateByFunction(Local<Function> function)
+	{
+		HandleScope handle_scope(isolate_);
+
+		bool bWasSuccessful = false;
+		for (auto it = functions.CreateIterator(); it; ++it)
+		{
+			if (Local<Function>::New(isolate_, it.Value())->Equals(function))
+			{
+				for (auto obj : DelegateObjects)
+				{
+					if (obj->UniqueId == it.Key())
+					{
+						return obj;
+					}
+				}
+			}
+		}
+
+		return nullptr;
+	}
+
+	void Remove(Local<Function> function)
+	{
+		auto obj = FindJavascriptDelegateByFunction(function);
+
+		if (obj)
+		{
+			Unbind(obj);
+		}
+		else
+		{
+			UE_LOG(Javascript, Log, TEXT("No match for removing delegate"));
+		}
+	}
+
+	void Clear()
+	{
+		while (DelegateObjects.Num())
+		{
+			Unbind(DelegateObjects[0]);
+		}
+	}
+
+	void Bind(UJavascriptDelegate* DelegateObject, Local<Function> function)
+	{
+		static FName NAME_Fire("Fire");
+
+		if (WeakObject.IsValid())
+		{
+			if (auto p = Cast<UMulticastDelegateProperty>(Property))
+			{
+				FScriptDelegate Delegate;
+				Delegate.BindUFunction(DelegateObject, NAME_Fire);
+
+				auto Target = p->GetPropertyValuePtr_InContainer(WeakObject.Get());
+				Target->Add(Delegate);
+			}
+			else if (auto p = Cast<UDelegateProperty>(Property))
+			{
+				auto Target = p->GetPropertyValuePtr_InContainer(WeakObject.Get());
+				Target->BindUFunction(DelegateObject, NAME_Fire);
+			}
+		}
+
+		DelegateObject->JavascriptDelegate = this;
+		DelegateObject->AddToRoot();
+		DelegateObjects.Add(DelegateObject);
+
+		functions.Add( DelegateObject->UniqueId, UniquePersistent<Function>(isolate_, function) );
+	}
+
+	void Unbind(UJavascriptDelegate* DelegateObject)
+	{
+		static FName NAME_Fire("Fire");
+
+		if (WeakObject.IsValid())
+		{
+			if (auto p = Cast<UMulticastDelegateProperty>(Property))
+			{
+				FScriptDelegate Delegate;
+				Delegate.BindUFunction(DelegateObject, NAME_Fire);
+
+				auto Target = p->GetPropertyValuePtr_InContainer(WeakObject.Get());
+				Target->Remove(Delegate);
+			}
+			else if (auto p = Cast<UDelegateProperty>(Property))
+			{
+				auto Target = p->GetPropertyValuePtr_InContainer(WeakObject.Get());
+				Target->Clear();
+			}
+		}
+
+		DelegateObject->JavascriptDelegate = nullptr;
+		DelegateObject->RemoveFromRoot();
+		DelegateObjects.Remove(DelegateObject);
+
+		if (!bAbandoned)
+		{
+			functions.Remove(DelegateObject->UniqueId);
+		}
+	}
+
+	UFunction* GetSignatureFunction()
+	{
+		if (auto p = Cast<UMulticastDelegateProperty>(Property))
+		{
+			return p->SignatureFunction;
+		}
+		else if (auto p = Cast<UDelegateProperty>(Property))
+		{
+			return p->SignatureFunction;
+		}
+		else
+		{
+			return nullptr;
+		}
+	}
+
+	void Fire(void* Parms, UJavascriptDelegate* Delegate)
+	{
+		auto Buffer = reinterpret_cast<uint8*>(Parms);
+
+		auto it = functions.Find(Delegate->UniqueId);
+		if (WeakObject.IsValid() && it)
+		{
+			Isolate::Scope isolate_scope(isolate_);
+			HandleScope handle_scope(isolate_);
+
+			auto func = Local<Function>::New(isolate_, *it);
+			if (!func.IsEmpty())
+			{
+				auto context = Local<Context>::New(isolate_, context_);
+
+				Context::Scope context_sopce(context);
+
+				CallJavascriptFunction(context, context->Global(), GetSignatureFunction(), func, Parms);
+			}
+		}
+	}
+};
+
+struct FDelegateManager : IDelegateManager
+{
+	Isolate* isolate_;
+
+	FDelegateManager(Isolate* isolate)
+		: isolate_(isolate)
+	{}
+
+	~FDelegateManager()
+	{
+		PurgeAllDelegates();
+	}
+
+	virtual void Destroy() override
+	{
+		delete this;
+	}
+
+	TSet<FJavascriptDelegate*> Delegates;
+
+	void CollectGarbageDelegates()
+	{
+		for (auto it = Delegates.CreateIterator(); it; ++it)
+		{
+			auto d = *it;
+			if (!d->IsValid())
+			{
+				delete d;
+				it.RemoveCurrent();
+			}
+		}
+	}
+
+	void PurgeAllDelegates()
+	{
+		for (auto d : Delegates)
+		{
+			delete d;
+		}
+		Delegates.Empty();
+	}
+
+	Local<Object> CreateDelegate(UObject* Object, UProperty* Property)
+	{
+		//@HACK
+		CollectGarbageDelegates();
+
+		auto payload = new FJavascriptDelegate(Object, Property);
+		auto created = payload->Initialize(isolate_->GetCurrentContext());
+
+		Delegates.Add(payload);
+
+		return created;
+	}
+
+	virtual Local<Value> GetProxy(Local<Object> This, UObject* Object, UProperty* Property) override
+	{
+		auto cache_id = V8_KeywordString(isolate_, FString::Printf(TEXT("$internal_%s"), *(Property->GetName())));
+		auto cached = This->Get(cache_id);
+		if (cached.IsEmpty() || cached->IsUndefined())
+		{
+			auto created = CreateDelegate(Object, Property);
+
+			This->Set(cache_id, created);
+			return created;
+		}
+		else
+		{
+			return cached;
+		}
+	}
+};
+
+namespace v8
+{
+	IDelegateManager* IDelegateManager::Create(Isolate* isolate)
+	{
+		return new FDelegateManager(isolate);
+	}
+}
+
+void UJavascriptDelegate::Fire()
+{}
+
+void UJavascriptDelegate::ProcessEvent(UFunction* Function, void* Parms)
+{
+	if (JavascriptDelegate)
+	{
+		JavascriptDelegate->Fire(Parms, this);
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Delegates.h b/Plugins/UnrealJS/Source/V8/Private/Delegates.h
new file mode 100644
index 0000000..5158c06
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Delegates.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace v8
+{
+	struct IDelegateManager
+	{
+		static IDelegateManager* Create(Isolate* isolate);
+		virtual void Destroy() = 0;
+		virtual Local<Value> GetProxy(Local<Object> This, UObject* Object, UProperty* Property) = 0;
+	};
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/DirectoryWatcher.cpp b/Plugins/UnrealJS/Source/V8/Private/DirectoryWatcher.cpp
new file mode 100644
index 0000000..3d13c0b
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/DirectoryWatcher.cpp
@@ -0,0 +1,67 @@
+#include "V8PCH.h"
+#include "DirectoryWatcher.h"
+#include "DirectoryWatcherModule.h"
+#include "IDirectoryWatcher.h"
+
+void UDirectoryWatcher::BeginDestroy()
+{
+	Super::BeginDestroy();
+
+	Unwatch();
+}
+
+bool UDirectoryWatcher::Contains(const FString& File)
+{
+	return Added.Find(File) != INDEX_NONE || Removed.Find(File) != INDEX_NONE || Modified.Find(File) != INDEX_NONE;
+}
+
+void UDirectoryWatcher::Watch(const FString& InDirectory)
+{	
+	Unwatch();
+
+	FDirectoryWatcherModule& DirectoryWatcherModule = FModuleManager::Get().LoadModuleChecked<FDirectoryWatcherModule>(TEXT("DirectoryWatcher"));
+	IDirectoryWatcher* DirectoryWatcher = DirectoryWatcherModule.Get();
+
+	Directory = FPaths::IsRelative(InDirectory) ? FPaths::ConvertRelativePathToFull(InDirectory) : InDirectory;
+
+	Changed = IDirectoryWatcher::FDirectoryChanged::CreateLambda([&](const TArray<FFileChangeData>& FileChanges){		
+		for (auto Change : FileChanges)
+		{
+			FPaths::NormalizeFilename(Change.Filename);
+			
+			switch (Change.Action)
+			{
+			case FFileChangeData::FCA_Added:
+				Added.Add(Change.Filename);
+				break;
+			case FFileChangeData::FCA_Modified:
+				Modified.Add(Change.Filename);
+				break;
+			case FFileChangeData::FCA_Removed:
+				Removed.Add(Change.Filename);
+				break;
+			}
+		}
+		OnChanged.Broadcast();
+		Added.Empty();
+		Modified.Empty();
+		Removed.Empty();
+	});	
+	
+	if (IFileManager::Get().DirectoryExists(*Directory))
+	{
+		DirectoryWatcher->RegisterDirectoryChangedCallback_Handle(Directory, Changed, DelegateHandle, true);
+	}	
+}
+
+void UDirectoryWatcher::Unwatch()
+{
+	FDirectoryWatcherModule& DirectoryWatcherModule = FModuleManager::Get().LoadModuleChecked<FDirectoryWatcherModule>(TEXT("DirectoryWatcher"));
+	IDirectoryWatcher* DirectoryWatcher = DirectoryWatcherModule.Get();
+
+	if (IFileManager::Get().DirectoryExists(*Directory))
+	{		
+		DirectoryWatcher->UnregisterDirectoryChangedCallback_Handle(Directory, DelegateHandle);
+		Directory = TEXT("");
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Exception.h b/Plugins/UnrealJS/Source/V8/Private/Exception.h
new file mode 100644
index 0000000..d610739
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Exception.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Translator.h"
+
+struct FV8Exception
+{
+	static void Report(TryCatch& try_catch)
+	{
+		using namespace v8;
+
+		auto exception = StringFromV8(try_catch.Exception());
+		auto message = try_catch.Message();
+		if (message.IsEmpty())
+		{
+			UE_LOG(Javascript, Error, TEXT("%s"), *exception);
+		}
+		else
+		{
+			auto filename = StringFromV8(message->GetScriptResourceName());
+			auto linenum = message->GetLineNumber();
+			auto line = StringFromV8(message->GetSourceLine());			
+
+			auto stack_trace = StringFromV8(try_catch.StackTrace());
+			if (stack_trace.Len() > 0)
+			{
+				TArray<FString> Lines;
+				stack_trace.ParseIntoArrayLines(Lines);
+				for (const auto& line : Lines)
+				{
+					UE_LOG(Javascript, Error, TEXT("%s"), *line);
+				}
+			}
+		}
+	}
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/FunctionCall.cpp b/Plugins/UnrealJS/Source/V8/Private/FunctionCall.cpp
new file mode 100644
index 0000000..fe7c49f
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/FunctionCall.cpp
@@ -0,0 +1,109 @@
+#include "V8PCH.h"
+#include "Translator.h"
+#include "Exception.h"
+#include "Helpers.h"
+
+namespace v8
+{
+	void CallJavascriptFunction(Handle<Context> context, Handle<Value> This, UFunction* SignatureFunction, Handle<Function> func, void* Parms)
+	{
+		auto isolate = context->GetIsolate();
+
+		HandleScope handle_scope(isolate);
+
+		auto Buffer = reinterpret_cast<uint8*>(Parms);		
+
+		enum { MaxArgs = 32 };
+
+		Handle<Value> argv[MaxArgs];
+		int argc = 0;
+
+		TFieldIterator<UProperty> Iter(SignatureFunction);
+		for (; Iter && argc < MaxArgs && (Iter->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++Iter)
+		{
+			UProperty* Param = *Iter;
+			argv[argc++] = ReadProperty(isolate, Param, Buffer, FNoPropertyOwner());
+		}
+
+		UProperty* ReturnParam = nullptr;
+		for (; Iter; ++Iter)
+		{
+			UProperty* Param = *Iter;
+			if (Param->GetPropertyFlags() & CPF_ReturnParm)
+			{
+				ReturnParam = Param;
+				break;
+			}
+		}
+
+		TryCatch try_catch;		
+
+		auto value = func->Call(This, argc, argv);
+
+		if (try_catch.HasCaught())
+		{
+			FV8Exception::Report(try_catch);
+		}
+
+		bool bHasAnyOutParams = false;
+		if (SignatureFunction && SignatureFunction->HasAnyFunctionFlags(FUNC_HasOutParms))
+		{
+			// Iterate over input parameters
+			for (TFieldIterator<UProperty> It(SignatureFunction); It && (It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++It)
+			{
+				// This is 'out ref'!
+				if ((It->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+				{
+					bHasAnyOutParams = true;
+					break;
+				}
+			}
+		}
+
+		if (bHasAnyOutParams)
+		{
+			FIsolateHelper I(isolate);
+			if (!value->IsObject())
+			{
+				I.Throw(TEXT("..."));
+				return;
+			}
+
+			auto Object = value->ToObject();
+
+			// Iterate over parameters again
+			for (TFieldIterator<UProperty> It(SignatureFunction); It; ++It)
+			{
+				UProperty* Param = *It;
+
+				auto PropertyFlags = Param->GetPropertyFlags();					
+
+				// pass return parameter as '$'
+				if (PropertyFlags & CPF_ReturnParm)
+				{
+					auto sub_value = Object->Get(I.Keyword("$"));
+
+					WriteProperty(isolate, ReturnParam, Buffer, sub_value);						
+				}
+				// rejects 'const T&' and pass 'T&' as its name
+				else if ((PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+				{
+					auto sub_value = Object->Get(I.Keyword(Param->GetName()));
+
+					if (!sub_value.IsEmpty())
+					{
+						// value can be null if isolate is in trouble
+						WriteProperty(isolate, Param, Buffer, sub_value);
+					}						
+				}
+			}			
+		}
+		else
+		{
+			if (ReturnParam)
+			{
+				WriteProperty(isolate, ReturnParam, Buffer, value);
+			}
+		}		
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Helpers.h b/Plugins/UnrealJS/Source/V8/Private/Helpers.h
new file mode 100644
index 0000000..981ae0d
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Helpers.h
@@ -0,0 +1,55 @@
+#pragma once
+
+struct FIsolateHelper
+{
+	v8::Isolate* isolate_;
+
+	FIsolateHelper(v8::Isolate* isolate)
+		: isolate_(isolate)
+	{}
+
+	FORCEINLINE v8::Local<v8::String> Keyword(const FString& String)
+	{
+		return V8_KeywordString(isolate_, String);
+	}
+
+	FORCEINLINE v8::Local<v8::String> Keyword(const char* String)
+	{
+		return V8_KeywordString(isolate_, String);
+	}
+
+	FORCEINLINE v8::Local<v8::String> String(const FString& InString)
+	{
+		return V8_String(isolate_, InString);
+	}
+
+	FORCEINLINE v8::Local<v8::String> String(const char* InString)
+	{
+		return V8_String(isolate_, InString);
+	}
+
+	FORCEINLINE v8::Local<External> External(void* Data)
+	{
+		return External::New(isolate_, Data);
+	}
+
+	FORCEINLINE v8::Local<v8::FunctionTemplate> FunctionTemplate()
+	{
+		return FunctionTemplate::New(isolate_);
+	}
+
+	FORCEINLINE v8::Local<v8::FunctionTemplate> FunctionTemplate(FunctionCallback Callback)
+	{
+		return FunctionTemplate::New(isolate_, Callback);
+	}
+
+	FORCEINLINE v8::Local<v8::FunctionTemplate> FunctionTemplate(FunctionCallback Callback, void* Data)
+	{
+		return FunctionTemplate::New(isolate_, Callback, External(Data));
+	}
+
+	void Throw(const FString& InString)
+	{
+		isolate_->ThrowException(v8::Exception::Error(String(InString)));
+	}
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptComponent.cpp b/Plugins/UnrealJS/Source/V8/Private/JavascriptComponent.cpp
new file mode 100644
index 0000000..a5866a2
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptComponent.cpp
@@ -0,0 +1,101 @@
+#include "V8PCH.h"
+#include "JavascriptComponent.h"
+#include "JavascriptIsolate.h"
+#include "JavascriptContext.h"
+#include "IV8.h"
+
+UJavascriptComponent::UJavascriptComponent(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+	PrimaryComponentTick.bCanEverTick = true;
+	bTickInEditor = false;
+	bAutoActivate = true;
+	bWantsInitializeComponent = true;
+}
+
+void UJavascriptComponent::OnRegister()
+{
+	auto ContextOwner = GetOuter();
+	if (ContextOwner && !HasAnyFlags(RF_ClassDefaultObject) && !ContextOwner->HasAnyFlags(RF_ClassDefaultObject))
+	{
+		if (GetWorld() && (GetWorld()->IsGameWorld() || bActiveWithinEditor))
+		{
+			auto Isolate = NewObject<UJavascriptIsolate>();
+			auto Context = Isolate->CreateContext();
+
+			JavascriptContext = Context;
+
+			Context->Expose("Root", this);
+			Context->Expose("GWorld", GetWorld());
+			Context->Expose("GEngine", GEngine);
+		}
+	}
+
+	Super::OnRegister();
+}
+
+void UJavascriptComponent::Activate(bool bReset)
+{
+	Super::Activate(bReset);
+
+	if (JavascriptContext)
+	{
+		JavascriptContext->RunFile(*ScriptSourceFile);
+
+		SetComponentTickEnabled(OnTick.IsBound());	
+	}
+
+	OnBeginPlay.ExecuteIfBound();
+}
+
+void UJavascriptComponent::Deactivate()
+{	
+	OnEndPlay.ExecuteIfBound();
+
+	Super::Deactivate();
+}
+
+void UJavascriptComponent::BeginDestroy()
+{
+	if (bIsActive)
+	{
+		Deactivate();
+	}
+
+	Super::BeginDestroy();
+}
+
+void UJavascriptComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
+{
+	check(bRegistered);
+
+	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+	OnTick.ExecuteIfBound(DeltaTime);
+}
+
+void UJavascriptComponent::ForceGC()
+{
+	JavascriptContext->RunScript(TEXT("gc();"));
+}
+
+void UJavascriptComponent::Expose(FString ExposedAs, UObject* Object)
+{
+	JavascriptContext->Expose(ExposedAs, Object);
+}
+
+void UJavascriptComponent::Invoke(FName Name)
+{
+	OnInvoke.ExecuteIfBound(Name);
+}
+
+void UJavascriptComponent::ProcessEvent(UFunction* Function, void* Parms)
+{
+	if (JavascriptContext && JavascriptContext->CallProxyFunction(this, this, Function, Parms))
+	{
+		return;
+	}
+
+	Super::ProcessEvent(Function, Parms);
+}
+
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptContext_Private.cpp b/Plugins/UnrealJS/Source/V8/Private/JavascriptContext_Private.cpp
new file mode 100644
index 0000000..a2702ae
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptContext_Private.cpp
@@ -0,0 +1,1239 @@
+#include "V8PCH.h"
+#include "JavascriptIsolate.h"
+#include "JavascriptContext.h"
+#include "JavascriptComponent.h"
+#include "FileManager.h"
+#include "Config.h"
+#include "Translator.h"
+#include "Exception.h"
+
+#include "JavascriptIsolate_Private.h"
+#include "JavascriptContext_Private.h"
+
+#if WITH_EDITOR
+#include "TypingGenerator.h"
+#endif
+
+#include "Helpers.h"
+#include "JavascriptGeneratedClass_Native.h"
+#include "JavascriptGeneratedClass.h"
+#include "JavascriptGeneratedFunction.h"
+
+using namespace v8;
+
+static const int kContextEmbedderDataIndex = 1;
+static const int32 MagicNumber = 0x2852abd3;
+
+static UProperty* CreateProperty(UObject* Outer, FString Decl)
+{
+	TArray<FString> SmallArray;
+	const TCHAR* Delims[] = {
+		TEXT("/*"),
+		TEXT("*/"),
+		TEXT("+")
+	};
+	Decl.ParseIntoArray(SmallArray, Delims, ARRAY_COUNT(Delims));
+
+	if (SmallArray.Num() < 2)
+	{
+		return nullptr;
+	}
+
+	FName Name = *SmallArray[0];
+	SmallArray.RemoveAt(0, 1);
+	FString Type = SmallArray.Pop();	
+	
+	auto SetupProperty = [&](UProperty* NewProperty) {
+		static struct FKeyword {
+			const TCHAR* Keyword;
+			uint64 Flags;
+		} Keywords[] = {
+			{ TEXT("Const"), CPF_ConstParm },
+			{ TEXT("Return"), CPF_ReturnParm },
+			{ TEXT("Out"), CPF_OutParm },
+			{ TEXT("Replicated"), CPF_Net },
+			{ TEXT("NotReplicated"), CPF_RepSkip },
+			{ TEXT("ReplicatedUsing"), CPF_Net | CPF_RepNotify },
+			{ TEXT("EditAnywhere"), CPF_Edit },
+			{ TEXT("EditDefaultsOnly"), CPF_Edit | CPF_DisableEditOnInstance },
+			{ TEXT("VisibleAnywhere"), CPF_Edit | CPF_EditConst },
+			{ TEXT("VisibleInstanceOnly"), CPF_Edit | CPF_EditConst | CPF_DisableEditOnTemplate },
+			{ TEXT("VisibleDefaultsOnly"), CPF_Edit | CPF_EditConst | CPF_DisableEditOnInstance },
+		};
+
+		for (const auto& Flag : SmallArray)
+		{
+			FString Left, Right;
+			if (!Flag.Split(TEXT(":"), &Left, &Right))
+			{
+				Left = Flag;
+			}
+
+			for (const auto& Keyword : Keywords)
+			{	
+				if (Left.Compare(Keyword.Keyword,ESearchCase::IgnoreCase) == 0)
+				{
+					NewProperty->SetPropertyFlags(Keyword.Flags);
+
+					if (Keyword.Flags & CPF_RepNotify)
+					{
+						NewProperty->RepNotifyFunc = FName(*Right);
+					}
+				}
+			}
+		}
+		
+		return NewProperty;
+	};
+
+	auto Create = [&]() -> UProperty* {
+		auto Inner = [&](UObject* Outer, const FString& Type) -> UProperty* {
+			// Find TypeObject (to make UObjectHash happy)
+			auto FindTypeObject = [](const TCHAR* ObjectName) {
+				const TCHAR* PackagesToSearch[] = {
+					TEXT("Engine"),
+					TEXT("CoreUObject")
+				};
+				UObject* TypeObject = nullptr;
+				for (auto PackageToSearch : PackagesToSearch)
+				{
+					TypeObject = StaticFindObject(UObject::StaticClass(), ANY_PACKAGE, *FString::Printf(TEXT("/Script/%s.%s"), PackageToSearch, ObjectName));
+					if (TypeObject) return TypeObject;
+				}
+				return StaticFindObject(UObject::StaticClass(), ANY_PACKAGE, ObjectName);
+			};
+
+			UObject* TypeObject = FindTypeObject(*Type);
+
+			if (auto p = Cast<UClass>(TypeObject))
+			{
+				auto q = NewObject<UObjectProperty>(Outer, Name);
+				q->SetPropertyClass(p);
+				return q;
+			}
+			else if (auto p = Cast<UScriptStruct>(TypeObject))
+			{
+				auto q = NewObject<UStructProperty>(Outer, Name);
+				q->Struct = p;
+				return q;
+			}
+			else if (Type == FString("bool"))
+			{
+				auto q = NewObject<UBoolProperty>(Outer, Name);
+				return q;
+			}
+			else if (Type == FString("int"))
+			{
+				auto q = NewObject<UIntProperty>(Outer, Name);
+				return q;
+			}
+			else if (Type == FString("string"))
+			{
+				auto q = NewObject<UStrProperty>(Outer, Name);
+				return q;
+			}
+			else if (Type == FString("float"))
+			{
+				auto q = NewObject<UFloatProperty>(Outer, Name);
+				return q;
+			}
+			else
+			{
+				auto q = NewObject<UInt64Property>(Outer, Name);
+				return q;
+			}
+		};
+
+		if (Type.EndsWith(TEXT("[]"))) 
+		{
+			auto q = NewObject<UArrayProperty>(Outer, Name);
+			q->Inner = Inner(q, Type.Left(Type.Len()-2));
+			return q;
+		} 
+		else
+		{
+			return Inner(Outer, Type);
+		}
+	};
+
+	return SetupProperty(Create());
+};
+
+static UProperty* DuplicateProperty(UObject* Outer, UProperty* Property, FName Name) 
+{
+	auto SetupProperty = [&](UProperty* NewProperty) {
+		NewProperty->SetPropertyFlags(Property->GetPropertyFlags());
+		return NewProperty;
+	};
+
+	auto Clone = [&]() -> UProperty* {
+		if (auto p = Cast<UStructProperty>(Property))
+		{
+			auto q = NewObject<UStructProperty>(Outer, Name);
+			q->Struct = p->Struct;
+			return q;
+		}
+		else if (auto p = Cast<UArrayProperty>(Property))
+		{
+			auto q = NewObject<UArrayProperty>(Outer, Name);
+			q->Inner = DuplicateProperty(q, p->Inner, p->Inner->GetFName());
+			return q;
+		}		
+		else if (auto p = Cast<UByteProperty>(Property))
+		{
+			auto q = NewObject<UByteProperty>(Outer, Name);
+			q->Enum = p->Enum;
+			return q;
+		}
+		else if (auto p = Cast<UBoolProperty>(Property))
+		{
+			auto q = NewObject<UBoolProperty>(Outer, Name);
+			q->SetBoolSize(sizeof(bool), true);
+			return q;
+		}
+		else if (auto p = Cast<UClassProperty>(Property))
+		{
+			auto q = NewObject<UClassProperty>(Outer, Name);
+			q->SetMetaClass(p->MetaClass);
+			q->PropertyClass = UClass::StaticClass();
+			return q;
+		}
+		else if (auto p = Cast<UObjectProperty>(Property))
+		{
+			auto q = NewObject<UObjectProperty>(Outer, Name);
+			q->SetPropertyClass(p->PropertyClass);
+			return q;
+		}
+		else
+		{
+			return static_cast<UProperty*>(StaticDuplicateObject(Property, Outer, *(Name.ToString())));
+		}
+	};
+
+	return SetupProperty(Clone());
+};
+
+void UJavascriptGeneratedFunction::Thunk(FFrame& Stack, RESULT_DECL)
+{
+	P_FINISH;
+
+	auto Function = static_cast<UJavascriptGeneratedFunction*>(Stack.CurrentNativeFunction);	
+
+	if (Function->JavascriptContext.IsValid())
+	{
+		auto Context = Function->JavascriptContext.Pin();
+
+		Isolate::Scope isolate_scope(Context->isolate());
+		HandleScope handle_scope(Context->isolate());
+		
+		bool bCallRet = Context->CallProxyFunction(Function->GetOuter(), this, Function, Stack.Locals);
+		if (bCallRet == false)
+			return;
+
+		UProperty* ReturnProp = ((UFunction*)Stack.Node)->GetReturnProperty();
+		if (ReturnProp != NULL)
+		{
+			const bool bHasReturnParam = Function->ReturnValueOffset != MAX_uint16;
+			uint8* ReturnValueAdress = bHasReturnParam ? (Stack.Locals + Function->ReturnValueOffset) : nullptr;
+			if (ReturnValueAdress)
+				FMemory::Memcpy(RESULT_PARAM, ReturnValueAdress, ReturnProp->ArrayDim * ReturnProp->ElementSize);
+		}
+
+		bool bHasAnyOutParams = false;
+		if (Function && Function->HasAnyFunctionFlags(FUNC_HasOutParms))
+		{
+			// Iterate over input parameters
+			for (TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++It)
+			{
+				// This is 'out ref'!
+				if ((It->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+				{
+					bHasAnyOutParams = true;
+					break;
+				}
+			}
+		}
+
+		if (bHasAnyOutParams)
+		{
+			auto OutParm = Stack.OutParms;
+
+			// Iterate over parameters again
+			for (TFieldIterator<UProperty> It(Function); It; ++It)
+			{
+				UProperty* Param = *It;
+
+				auto PropertyFlags = Param->GetPropertyFlags();
+				if ((PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+				{
+					auto Property = OutParm->Property;
+					if (Property != nullptr)
+					{
+						auto ValueAddress = Property->ContainerPtrToValuePtr<uint8>(Stack.Locals);
+						FMemory::Memcpy(OutParm->PropAddr, ValueAddress, Property->ArrayDim * Property->ElementSize);						
+					}
+				}
+
+				if (PropertyFlags & CPF_OutParm)
+					OutParm = OutParm->NextOutParm;
+			}
+		}
+	}
+}
+
+class FJavascriptContextImplementation : public FJavascriptContext
+{
+	friend class UJavascriptContext;
+
+	const FObjectInitializer* ObjectInitializer{ nullptr };
+
+	virtual const FObjectInitializer* GetObjectInitializer() override 
+	{
+		return ObjectInitializer;
+	}
+
+	int32 Magic{ MagicNumber };	
+
+	Persistent<Context> context_;
+	IJavascriptDebugger* debugger{ nullptr };
+
+	TMap<FString, UObject*> WKOs;
+
+public:
+	Isolate* isolate() { return Environment->isolate_; }
+	Local<Context> context() { return Local<Context>::New(isolate(), context_); }
+	bool IsValid() const { return Magic == MagicNumber; }
+
+public:
+
+	TMap<FString, UniquePersistent<Value>> Modules;
+	TArray<FString>& Paths;
+
+	void SetAsDebugContext()
+	{
+		if (debugger) return;
+
+		Isolate::Scope isolate_scope(isolate());
+		HandleScope handle_scope(isolate());
+
+		debugger = IJavascriptDebugger::Create(5858, context());
+	}
+
+	bool IsDebugContext() const
+	{
+		return debugger != nullptr;
+	}
+
+	void ResetAsDebugContext()
+	{
+		if (debugger)
+		{
+			Isolate::Scope isolate_scope(isolate());
+			HandleScope handle_scope(isolate());
+
+			debugger->Destroy();
+			debugger = nullptr;
+		}
+	}
+
+	FJavascriptContextImplementation(TSharedPtr<FJavascriptIsolate> InEnvironment, TArray<FString>& InPaths)
+		: FJavascriptContext(InEnvironment), Paths(InPaths)
+	{
+		Isolate::Scope isolate_scope(isolate());
+		HandleScope handle_scope(isolate());
+
+		auto context = Context::New(isolate(), nullptr, Environment->GetGlobalTemplate());
+		context->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, this);
+
+		context_.Reset(isolate(), context);
+
+		ExposeGlobals();
+
+		Paths = IV8::Get().GetGlobalScriptSearchPaths();
+	}
+
+	~FJavascriptContextImplementation()
+	{
+		PurgeModules();
+
+		ReleaseAllPersistentHandles();
+
+		ResetAsDebugContext();
+
+		context_.Reset();		
+	}
+
+	void ReleaseAllPersistentHandles()
+	{
+		// Release all object instances
+		ObjectToObjectMap.Empty();
+
+		// Release all struct instances
+		MemoryToObjectMap.Empty();
+	}
+
+	void ExposeGlobals()
+	{
+		HandleScope handle_scope(isolate());
+		Context::Scope context_scope(context());
+
+		ExposeRequire();
+		ExportUnrealEngineClasses();
+	}
+
+	void PurgeModules()
+	{
+		Modules.Empty();
+	}
+
+	void ExportUnrealEngineClasses()
+	{
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto Context = reinterpret_cast<FJavascriptContextImplementation*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			FIsolateHelper I(isolate);
+
+			HandleScope scope(isolate);
+
+			auto Name = StringFromV8(info[0]);
+			auto Opts = info[1]->ToObject();
+			auto Outer = UObjectFromV8(Opts->Get(I.Keyword("Outer")));
+			auto ParentClass = UClassFromV8(isolate, Opts->Get(I.Keyword("Parent")));
+			Outer = Outer ? Outer : GetTransientPackage();
+			ParentClass = ParentClass ? ParentClass : UObject::StaticClass();
+
+			UBlueprintGeneratedClass* Class = nullptr;
+			if (Cast<UBlueprintGeneratedClass>(ParentClass))
+			{
+				auto Klass = NewObject<UJavascriptGeneratedClass>(Outer, *Name, RF_Public);
+				Klass->JavascriptContext = Context->AsShared();				
+				Class = Klass;
+			}
+			else 
+			{
+				auto Klass = NewObject<UJavascriptGeneratedClass_Native>(Outer, *Name, RF_Public);
+				Klass->JavascriptContext = Context->AsShared();
+				Class = Klass;
+
+				// This flag is necessary for proper initialization
+				Class->ClassFlags |= CLASS_Native;
+			}
+
+			// Create a blueprint
+			auto Blueprint = NewObject<UBlueprint>(Outer);
+			Blueprint->GeneratedClass = Class;
+			Class->ClassGeneratedBy = Blueprint;			
+
+			auto ClassConstructor = [](const FObjectInitializer& ObjectInitializer){
+				auto Class = static_cast<UBlueprintGeneratedClass*>(ObjectInitializer.GetClass());
+				Class->GetSuperClass()->ClassConstructor(ObjectInitializer);
+
+				FJavascriptContextImplementation* Context = nullptr;
+
+				if (auto Klass = Cast<UJavascriptGeneratedClass_Native>(Class)) 
+				{
+					if (Klass->JavascriptContext.IsValid())
+					{
+						Context = static_cast<FJavascriptContextImplementation*>(Klass->JavascriptContext.Pin().Get());
+					}
+				}
+				else if (auto Klass = Cast<UJavascriptGeneratedClass>(Class))
+				{
+					if (Klass->JavascriptContext.IsValid())
+					{
+						Context = static_cast<FJavascriptContextImplementation*>(Klass->JavascriptContext.Pin().Get());
+					}
+				}
+
+
+				if (Context)
+				{
+					auto isolate = Context->isolate();
+					auto Object = ObjectInitializer.GetObj();
+
+					FIsolateHelper I(isolate);
+
+					Isolate::Scope isolate_scope(isolate);
+					HandleScope handle_scope(isolate);
+					Context::Scope context_scope(Context->context());
+
+					auto Holder = Context->ExportObject(Class);					
+
+					auto v8_obj = Holder->ToObject();
+					auto proxy = v8_obj->Get(I.Keyword("proxy"));
+					if (proxy.IsEmpty() || !proxy->IsObject())
+					{
+						I.Throw(TEXT("Invalid proxy : construct class"));
+						return;
+					}
+					auto func = proxy->ToObject()->Get(I.Keyword("ctor"));					
+
+					Context->ObjectInitializer = &ObjectInitializer;
+
+					auto This = Context->ExportObject(Object);
+
+					auto context = Context->context();
+
+					if (func->IsFunction())
+					{
+						CallJavascriptFunction(context, This, nullptr, Local<Function>::Cast(func), nullptr);
+					}
+
+					Context->ObjectInitializer = nullptr;
+				}
+			};
+
+			Class->ClassConstructor = ClassConstructor;
+			
+			// Set properties we need to regenerate the class with
+			Class->PropertyLink = ParentClass->PropertyLink;
+			Class->ClassWithin = ParentClass->ClassWithin;
+			Class->ClassConfigName = ParentClass->ClassConfigName;
+			
+			Class->SetSuperStruct(ParentClass);
+			Class->ClassFlags |= (ParentClass->ClassFlags & (CLASS_Inherit | CLASS_ScriptInherit | CLASS_CompiledFromBlueprint));
+			Class->ClassCastFlags |= ParentClass->ClassCastFlags;			
+
+			auto AddFunction = [&](FName NewFunctionName, Handle<Value> TheFunction) -> bool {
+				UFunction* ParentFunction = ParentClass->FindFunctionByName(NewFunctionName);
+				
+				UJavascriptGeneratedFunction* Function{ nullptr };
+
+				auto MakeFunction = [&]() {
+					Function = NewObject<UJavascriptGeneratedFunction>(Class, NewFunctionName, RF_Public);
+					Function->JavascriptContext = Context->AsShared();
+					Function->RepOffset = MAX_uint16;
+					Function->ReturnValueOffset = MAX_uint16;
+					Function->FirstPropertyToInit = NULL;
+
+					Function->Script.Add(EX_Nothing);
+				};
+
+				// Overridden function should have its parent function
+				if (ParentFunction)
+				{				
+					MakeFunction();
+
+					Function->SetSuperStruct(ParentFunction);					
+
+					auto InitializeProperties = [](UFunction* Function, UFunction* ParentFunction) {
+						UField** Storage = &Function->Children;
+						UProperty** PropertyStorage = &Function->PropertyLink;
+
+						for (TFieldIterator<UProperty> PropIt(ParentFunction, EFieldIteratorFlags::ExcludeSuper); PropIt; ++PropIt)
+						{
+							UProperty* Property = *PropIt;
+							if (Property->HasAnyPropertyFlags(CPF_Parm))
+							{
+								UProperty* NewProperty = DuplicateProperty(Function, Property, Property->GetFName());
+
+								*Storage = NewProperty;
+								Storage = &NewProperty->Next;
+
+								*PropertyStorage = NewProperty;
+								PropertyStorage = &NewProperty->PropertyLinkNext;
+							}
+						}
+					};
+
+					if (ParentFunction)
+					{
+						InitializeProperties(Function, ParentFunction);
+					}
+				}
+				else
+				{
+					TArray<FString> Arguments;
+
+					{
+						FString TheString = StringFromV8(TheFunction->ToString());
+
+						int32 Index;						
+
+						if (TheString.FindChar('(', Index))
+						{
+							TheString = TheString.Mid(Index + 1);
+						}
+
+						if (TheString.FindChar(')', Index))
+						{
+							FString Rest = TheString.Mid(Index + 1);
+							TheString = TheString.Left(Index);
+
+							if (Rest.FindChar('{', Index))
+							{
+								Rest = Rest.Left(Index);
+
+								TArray<FString> SmallArray;
+								const TCHAR* Delims[] = {
+									TEXT("/*"),
+									TEXT("*/"),
+									TEXT("+")
+								};
+
+								static struct FKeyword {
+									const TCHAR* Keyword;
+									int32 Flags;
+								} Keywords[] = {									
+									{ TEXT("Exec"), FUNC_Exec },
+									{ TEXT("Server"), FUNC_Net | FUNC_NetServer},
+									{ TEXT("Client"), FUNC_Net | FUNC_NetClient },
+									{ TEXT("NetMulticast"), FUNC_Net | FUNC_NetMulticast },
+									{ TEXT("Reliable"), FUNC_NetReliable },
+									{ TEXT("Unreliable"), FUNC_NetResponse }
+								};								
+									
+								Rest.Trim().ParseIntoArray(SmallArray, Delims, ARRAY_COUNT(Delims));
+
+								// If this function is not a UFunction-candiate?
+								if (SmallArray.Num() == 0 && !TheString.Contains(TEXT("/*")))
+								{
+									return false;
+								}
+
+								MakeFunction();
+								
+								for (const auto& Flag : SmallArray)
+								{
+									for (const auto& Keyword : Keywords)
+									{
+										// Partial match is allowed
+										if (Flag.Contains(Keyword.Keyword))
+										{
+											Function->FunctionFlags |= Keyword.Flags;
+										}
+									}
+								}
+							}							
+						}
+
+						TArray<FString> Array;
+						TheString.ParseIntoArray(Array, TEXT(","));
+
+						for (auto Item : Array)
+						{
+							Arguments.Add(Item);							
+						}
+					}
+
+					auto InitializeProperties = [](UFunction* Function, const TArray<FString>& Arguments) {
+						UField** Storage = &Function->Children;
+						UProperty** PropertyStorage = &Function->PropertyLink;
+
+						for (const auto& Argument : Arguments)
+						{
+							UProperty* NewProperty = CreateProperty(Function, Argument);
+
+							if (NewProperty)
+							{
+								NewProperty->SetPropertyFlags(CPF_Parm);
+								
+								*Storage = NewProperty;
+								Storage = &NewProperty->Next;
+
+								*PropertyStorage = NewProperty;
+								PropertyStorage = &NewProperty->PropertyLinkNext;								
+							}
+							else
+							{
+								// ??
+							}
+						}
+					};
+
+					InitializeProperties(Function, Arguments);					
+				}
+
+				auto FinalizeFunction = [](UFunction* Function) {
+					Function->Bind();
+					Function->StaticLink(true);
+
+					Function->FunctionFlags |= FUNC_Native;
+
+					for (TFieldIterator<UProperty> PropIt(Function, EFieldIteratorFlags::ExcludeSuper); PropIt; ++PropIt)
+					{
+						UProperty* Property = *PropIt;
+						if (Property->HasAnyPropertyFlags(CPF_Parm))
+						{
+							++Function->NumParms;
+							Function->ParmsSize = Property->GetOffset_ForUFunction() + Property->GetSize();
+
+							if (Property->HasAnyPropertyFlags(CPF_OutParm))
+							{
+								Function->FunctionFlags |= FUNC_HasOutParms;
+							}
+
+							if (Property->HasAnyPropertyFlags(CPF_ReturnParm))
+							{
+								Function->ReturnValueOffset = Property->GetOffset_ForUFunction();
+
+								if (!Property->HasAnyPropertyFlags(CPF_IsPlainOldData | CPF_NoDestructor))
+								{
+									Property->DestructorLinkNext = Function->DestructorLink;
+									Function->DestructorLink = Property;
+								}
+							}
+						}
+						else
+						{
+							if (!Property->HasAnyPropertyFlags(CPF_ZeroConstructor))
+							{
+								Function->FirstPropertyToInit = Property;
+								Function->FunctionFlags |= FUNC_HasDefaults;
+								break;
+							}
+						}
+					}
+				};
+
+				FinalizeFunction(Function);
+
+				Function->SetNativeFunc((Native)&UJavascriptGeneratedFunction::Thunk);
+
+				Function->Next = Class->Children;
+				Class->Children = Function;
+
+				// Add the function to it's owner class function name -> function map
+				Class->AddFunctionToFunctionMap(Function);
+
+				return true;
+			};
+
+			auto Functions = Opts->Get(I.Keyword("Functions"));
+			TMap<FString,Handle<Value>> Others;
+			if (!Functions.IsEmpty() && Functions->IsObject())
+			{
+				auto FuncMap = Functions->ToObject();
+				auto Keys = FuncMap->GetOwnPropertyNames();
+
+				auto NumKeys = Keys->Length();
+
+				for (decltype(NumKeys) Index = 0; Index < NumKeys; ++Index)
+				{
+					auto Name = Keys->Get(Index);
+					auto UName = StringFromV8(Name);
+					auto Function = FuncMap->Get(Name);
+
+					if (!Function->IsFunction()) continue;
+
+					if (UName == TEXT("properties"))
+					{						
+						FString Code = StringFromV8(Function->ToString());
+
+						TArray<FString> SmallArray;
+						const TCHAR* Delims[] = {
+							TEXT("\n"),
+							TEXT(";")
+						};
+						
+						Code.ParseIntoArray(SmallArray, Delims, ARRAY_COUNT(Delims));
+
+						for (auto Line : SmallArray)
+						{
+							Line = Line.Trim();
+
+							if (Line.StartsWith(TEXT("this.")))
+							{
+								auto Property = CreateProperty(Class, Line.Mid(5));
+								if (Property)
+								{
+									Class->AddCppProperty(Property);
+
+									if (Property->HasAnyPropertyFlags(CPF_Net))
+									{
+										Class->NumReplicatedProperties++;
+									}
+								}
+							}
+						}
+					}
+					else if (UName != TEXT("ctor") && UName != TEXT("constructor"))
+					{
+						if (!AddFunction(*UName, Function))
+						{						
+							Others.Add(UName, Function);
+						}
+					}
+				}
+			}
+
+			Class->Bind();
+			Class->StaticLink(true);
+
+			{
+				auto FinalClass = Context->Environment->ExportClass(Class,false);
+				auto Prototype = FinalClass->PrototypeTemplate();
+
+				for (auto It = Others.CreateIterator(); It; ++It)
+				{
+					Prototype->Set(I.Keyword(It.Key()), It.Value());
+				}				
+
+				Context->Environment->RegisterClass(Class, FinalClass);
+			}
+
+			auto FinalClass = Context->ExportObject(Class);
+			FinalClass->ToObject()->Set(I.Keyword("proxy"), Functions);
+
+			info.GetReturnValue().Set(FinalClass);
+
+			// Make sure CDO is ready for use
+			Class->GetDefaultObject();
+		};
+
+		auto global = context()->Global();
+		auto self = External::New(isolate(), this);
+
+		global->Set(V8_KeywordString(isolate(), "CreateClass"), FunctionTemplate::New(isolate(), fn, self)->GetFunction());
+	}
+
+	void ExposeRequire()
+	{
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto isolate = info.GetIsolate();
+			HandleScope scope(isolate);
+
+			if (info.Length() != 1 || !(info[0]->IsString()))
+			{
+				return;
+			}
+
+			auto Self = reinterpret_cast<FJavascriptContextImplementation*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto required_module = StringFromV8(info[0]);
+
+			bool found = false;
+
+			auto inner = [&](const FString& script_path)
+			{				
+				auto it = Self->Modules.Find(script_path);
+				if (it)
+				{
+					info.GetReturnValue().Set(Local<Value>::New(isolate, *it));
+					found = true;
+					return true;
+				}
+
+				FString Text;
+				if (FFileHelper::LoadFileToString(Text, *script_path))
+				{
+					Text = FString::Printf(TEXT("(function (__dirname) {\nvar module = { exports : {}, filename : __dirname }, exports = module.exports;\n%s\n;return module.exports;}('%s'));"), *Text, *script_path);
+					auto full_path = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*script_path);
+#if PLATFORM_WINDOWS
+					full_path = full_path.Replace(TEXT("/"), TEXT("\\"));
+#endif
+					auto exports = Self->RunScript(full_path, Text, 2);
+					if (exports.IsEmpty())
+					{
+						UE_LOG(Javascript, Log, TEXT("Invalid script for require"));
+					}
+					Self->Modules.Add(script_path, UniquePersistent<Value>(isolate, exports));
+					info.GetReturnValue().Set(exports);
+					found = true;
+					return true;
+				}
+
+				return false;
+			};
+
+			auto inner_package = [&](const FString& script_path)
+			{
+				FString Text;
+				if (FFileHelper::LoadFileToString(Text, *(script_path / TEXT("package.json"))))
+				{
+					Text = FString::Printf(TEXT("(function (json) {return json.main;})(%s);"), *Text);
+					auto full_path = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*script_path);
+#if PLATFORM_WINDOWS
+					full_path = full_path.Replace(TEXT("/"), TEXT("\\"));
+#endif
+					auto exports = Self->RunScript(full_path, Text, 0);
+					if (exports.IsEmpty() || !exports->IsString())
+					{
+						return false;
+					}
+					else
+					{
+						return inner(script_path / StringFromV8(exports));
+					}
+				}
+
+				return false;
+			};
+
+			auto inner2 = [&](FString base_path)
+			{
+				for (;;)
+				{
+					if (!FPaths::DirectoryExists(base_path)) return false;
+
+					auto script_path = base_path / required_module;
+					if (script_path.Find(TEXT(".js")) == INDEX_NONE)
+					{
+						if (inner(script_path + TEXT(".js"))) return true;
+						if (inner(script_path / TEXT("index.js"))) return true;
+
+						if (inner_package(script_path)) return true;
+					}
+					else
+					{
+						if (inner(script_path)) return true;
+					}
+
+					base_path = base_path / TEXT("node_modules");
+				}
+			};
+
+			auto current_script_path = FPaths::GetPath(StringFromV8(StackTrace::CurrentStackTrace(isolate, 1, StackTrace::kScriptName)->GetFrame(0)->GetScriptName()));
+#if PLATFORM_WINDOWS
+			current_script_path = current_script_path.Replace(TEXT("\\"), TEXT("/"));
+#endif
+			if (!inner2(current_script_path))
+			{
+				for (const auto& path : Self->Paths)
+				{
+					if (inner2(path)) break;
+				}
+			}
+
+			if (!found)
+			{
+				info.GetReturnValue().Set(Undefined(isolate));
+			}
+		};
+
+		auto fn2 = [](const FunctionCallbackInfo<Value>& info) {
+			auto isolate = info.GetIsolate();
+			HandleScope scope(isolate);
+
+			auto Self = reinterpret_cast<FJavascriptContextImplementation*>((Local<External>::Cast(info.Data()))->Value());
+			Self->PurgeModules();
+		};
+
+		auto global = context()->Global();
+		auto self = External::New(isolate(), this);
+
+		global->Set(V8_KeywordString(isolate(), "require"), FunctionTemplate::New(isolate(), fn, self)->GetFunction());
+		global->Set(V8_KeywordString(isolate(), "purge_modules"), FunctionTemplate::New(isolate(), fn2, self)->GetFunction());
+
+		auto getter = [](Local<String> property, const PropertyCallbackInfo<Value>& info) {
+			auto isolate = info.GetIsolate();
+			HandleScope scope(isolate);
+
+			auto Self = reinterpret_cast<FJavascriptContextImplementation*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto out = Object::New(isolate);
+
+			for (auto it = Self->Modules.CreateConstIterator(); it; ++it)
+			{
+				const auto& name = it.Key();
+				const auto& module = it.Value();
+
+				auto FullPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*name);
+				out->Set(V8_String(isolate, name), V8_String(isolate, TCHAR_TO_UTF8(*FullPath)));
+			}
+
+			info.GetReturnValue().Set(out);
+		};
+		global->SetAccessor(V8_KeywordString(isolate(), "modules"), getter, 0, self);
+	}
+
+	FString GetScriptFileFullPath(const FString& Filename)
+	{
+		for (auto Path : Paths)
+		{
+			auto FullPath = Path / Filename;
+			auto Size = IFileManager::Get().FileSize(*FullPath);
+			if (Size != INDEX_NONE)
+			{
+				return IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FullPath);
+			}
+		}
+		return Filename;
+	}
+
+	FString ReadScriptFile(const FString& Filename)
+	{
+		auto Path = GetScriptFileFullPath(Filename);
+
+		FString Text;
+
+		FFileHelper::LoadFileToString(Text, *Path);
+
+		return Text;
+	}
+
+	Local<Value> RunFile(const FString& Filename)
+	{
+		HandleScope handle_scope(isolate());
+
+		auto Script = ReadScriptFile(Filename);
+
+		return RunScript(GetScriptFileFullPath(Filename), Script);
+	}
+
+	void Public_RunFile(const FString& Filename)
+	{
+		RunFile(Filename);
+	}
+
+	FString Public_RunScript(const FString& Script, bool bOutput = true)
+	{
+		Isolate::Scope isolate_scope(isolate());
+		HandleScope handle_scope(isolate());
+		Context::Scope context_scope(context());
+
+		auto ret = RunScript(TEXT("(inline)"), Script);
+		auto str = ret.IsEmpty() ? TEXT("(empty)") : StringFromV8(ret);
+
+		if (bOutput && !ret.IsEmpty())
+		{
+			UE_LOG(Javascript, Log, TEXT("%s"), *str);
+		}		
+		return str;
+	}
+
+	// Should be guarded with proper handle scope
+	Local<Value> RunScript(const FString& Filename, const FString& Script, int line_offset = 0)
+	{
+		Isolate::Scope isolate_scope(isolate());
+		Context::Scope context_scope(context());
+
+		TryCatch try_catch;
+		try_catch.SetVerbose(true);
+
+		auto Path = Filename;
+		auto source = V8_String(isolate(), Script);
+		auto path = V8_String(isolate(), Path);
+		ScriptOrigin origin(path, Integer::New(isolate(), -line_offset));
+		auto script = Script::Compile(source, &origin);
+
+		auto result = script->Run();
+		if (try_catch.HasCaught())
+		{
+			FV8Exception::Report(try_catch);
+			return Local<Value>();
+		}
+		else
+		{
+			return result;
+		}
+	}
+
+	void Expose(FString RootName, UObject* Object)
+	{
+		WKOs.Add(RootName, Object);
+
+		auto RootGetter = [](Local<String> property, const PropertyCallbackInfo<Value>& info) {
+			auto isolate = info.GetIsolate();
+			info.GetReturnValue().Set(info.Data());
+		};
+
+		Isolate::Scope isolate_scope(isolate());
+		HandleScope handle_scope(isolate());
+		Context::Scope context_scope(context());
+
+		context()->Global()->SetAccessor(V8_KeywordString(isolate(), RootName), RootGetter, 0, ExportObject(Object));
+	}
+
+	Local<Value> ExportObject(UObject* Object, bool bForce = false) override
+	{
+		return Environment->ExportObject(Object, bForce);
+	}
+
+	bool WriteAliases(const FString& Filename)
+	{
+#if WITH_EDITOR
+		struct TokenWriter
+		{
+			FString Text;
+
+			TokenWriter& push(const char* something)
+			{
+				Text.Append(ANSI_TO_TCHAR(something));
+				return *this;
+			}
+
+			TokenWriter& push(const FString& something)
+			{
+				Text.Append(something);
+				return *this;
+			}
+
+			const TCHAR* operator * ()
+			{
+				return *Text;
+			}
+		};
+
+		TokenWriter w;
+
+		for (auto it = Environment->ClassToFunctionTemplateMap.CreateConstIterator(); it; ++it)
+		{
+			const UClass* ClassToExport = it.Key();
+
+			// Skip a generated class
+			if (ClassToExport->ClassGeneratedBy) continue;
+
+			auto ClassName = FV8Config::Safeify(ClassToExport->GetName());
+
+			TArray<UFunction*> Functions;
+			Environment->BlueprintFunctionLibraryMapping.MultiFind(ClassToExport, Functions);
+
+			auto conditional_emit_alias = [&](UFunction* Function, bool is_thunk) {
+				auto Alias = FV8Config::GetAlias(Function);
+				if (FV8Config::CanExportFunction(ClassToExport, Function) && Alias.Len() > 0)
+				{
+					w.push(ClassName);
+					w.push(".prototype.");
+					w.push(Alias);
+					w.push(" = ");
+					w.push(ClassName);
+					w.push(".prototype.");
+					w.push(FV8Config::Safeify(Function->GetName()));
+					w.push(";\n");
+
+					if (!is_thunk && Function->FunctionFlags & FUNC_Static)
+					{
+						w.push(ClassName);
+						w.push(".");
+						w.push(Alias);
+						w.push(" = ");
+						w.push(ClassName);
+						w.push(".");
+						w.push(FV8Config::Safeify(Function->GetName()));
+						w.push(";\n");
+					}
+				}
+			};
+
+			for (auto Function : Functions)
+			{
+				conditional_emit_alias(Function, true);
+			}
+
+			for (TFieldIterator<UFunction> FuncIt(ClassToExport, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
+			{
+				conditional_emit_alias(*FuncIt, false);
+			}
+		}
+
+		return FFileHelper::SaveStringToFile(*w, *Filename);
+#else
+		return false;
+#endif
+	}
+
+	bool WriteDTS(const FString& Filename)
+	{
+#if WITH_EDITOR
+		TypingGenerator instance(*(Environment.Get()));
+
+		instance.ExportBootstrap();
+
+		for (auto it = Environment->ClassToFunctionTemplateMap.CreateConstIterator(); it; ++it)
+		{
+			instance.Export(it.Key());
+		}
+
+		for (auto pair : WKOs)
+		{
+			auto k = pair.Key;
+			auto v = pair.Value;
+
+			instance.ExportWKO(k, v);
+		}
+
+		instance.Finalize();
+
+		return instance.Save(Filename);
+#else
+		return false;
+#endif
+	}
+
+	Local<Value> GetProxyFunction(UObject* Object, const TCHAR* Name)
+	{
+		auto v8_obj = ExportObject(Object)->ToObject();
+		auto proxy = v8_obj->Get(V8_KeywordString(isolate(), "proxy"));
+		if (proxy.IsEmpty() || !proxy->IsObject())
+		{
+			return Undefined(isolate());
+		}
+
+		auto func = proxy->ToObject()->Get(V8_KeywordString(isolate(), Name));
+		if (func.IsEmpty() || !func->IsFunction())
+		{
+			return Undefined(isolate());
+		}
+
+		return func;
+	}
+
+	Local<Value> GetProxyFunction(UObject* Object, UFunction* Function)
+	{
+		return GetProxyFunction(Object, *FV8Config::Safeify(Function->GetName()));
+	}
+
+	bool HasProxyFunction(UObject* Holder, UFunction* Function)
+	{
+		Isolate::Scope isolate_scope(isolate());
+		HandleScope handle_scope(isolate());
+
+		auto func = GetProxyFunction(Holder, Function);;
+		return !func.IsEmpty() && func->IsFunction();
+	}
+
+	bool CallProxyFunction(UObject* Holder, UObject* This, UFunction* FunctionToCall, void* Parms)
+	{
+		Isolate::Scope isolate_scope(isolate());
+		HandleScope handle_scope(isolate());
+
+		Context::Scope context_scope(context());
+
+		auto func = GetProxyFunction(Holder, FunctionToCall);
+		if (!func.IsEmpty() && func->IsFunction())
+		{
+			CallJavascriptFunction(context(), This ? ExportObject(This) : context()->Global(), FunctionToCall, Local<Function>::Cast(func), Parms);
+
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	// To tell Unreal engine's GC not to destroy these objects!
+	virtual void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) override
+	{		
+		Public_RunScript(TEXT("gc();"), false);
+
+		// All objects
+		for (auto It = ObjectToObjectMap.CreateIterator(); It; ++It)
+		{
+			Collector.AddReferencedObject(It.Key(), InThis);
+		}
+	}
+};
+
+FJavascriptContext* FJavascriptContext::FromV8(v8::Local<v8::Context> Context)
+{
+	if (Context.IsEmpty()) return nullptr;
+	
+	auto Instance = reinterpret_cast<FJavascriptContextImplementation*>(Context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex));
+	if (Instance->IsValid())
+	{
+		return Instance;
+	}
+	else
+	{
+		return nullptr;
+	}
+}
+
+FJavascriptContext* FJavascriptContext::Create(TSharedPtr<FJavascriptIsolate> InEnvironment, TArray<FString>& InPaths)
+{
+	return new FJavascriptContextImplementation(InEnvironment, InPaths);
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptContext_Private.h b/Plugins/UnrealJS/Source/V8/Private/JavascriptContext_Private.h
new file mode 100644
index 0000000..a42645e
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptContext_Private.h
@@ -0,0 +1,41 @@
+#pragma once
+
+struct FJavascriptContext : TSharedFromThis<FJavascriptContext>
+{
+	FJavascriptContext(TSharedPtr<FJavascriptIsolate> InEnvironment) : Environment(InEnvironment) {}
+
+	/** Isolate **/
+	TSharedPtr<FJavascriptIsolate> Environment;
+
+	/** A map from Unreal UObject to V8 Object */
+	TMap< UObject*, v8::UniquePersistent<v8::Value> > ObjectToObjectMap;
+
+	/** A map from Struct buffer to V8 Object */
+	TMap< TSharedPtr<FStructMemoryInstance>, v8::UniquePersistent<v8::Value> > MemoryToObjectMap;
+
+	virtual ~FJavascriptContext() {}
+	virtual void Expose(FString RootName, UObject* Object) = 0;
+	virtual FString GetScriptFileFullPath(const FString& Filename) = 0;
+	virtual FString ReadScriptFile(const FString& Filename) = 0;
+	virtual FString Public_RunScript(const FString& Script, bool bOutput = true) = 0;
+	virtual void Public_RunFile(const FString& Filename) = 0;
+	virtual void SetAsDebugContext() = 0;
+	virtual bool IsDebugContext() const = 0;
+	virtual bool WriteAliases(const FString& Filename) = 0;
+	virtual bool WriteDTS(const FString& Filename) = 0;
+	virtual bool HasProxyFunction(UObject* Holder, UFunction* Function) = 0;
+	virtual bool CallProxyFunction(UObject* Holder, UObject* This, UFunction* FunctionToCall, void* Parms) = 0;	
+
+	virtual Isolate* isolate() = 0;
+	virtual Local<Context> context() = 0;
+	virtual Local<Value> ExportObject(UObject* Object, bool bForce = false) = 0;
+	virtual Local<Value> GetProxyFunction(UObject* Object, const TCHAR* Name) = 0;
+
+	static FJavascriptContext* FromV8(v8::Local<v8::Context> Context);
+
+	static FJavascriptContext* Create(TSharedPtr<FJavascriptIsolate> InEnvironment, TArray<FString>& InPaths);
+
+	virtual void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) = 0;
+
+	virtual const FObjectInitializer* GetObjectInitializer() = 0;
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptIsolate_Private.cpp b/Plugins/UnrealJS/Source/V8/Private/JavascriptIsolate_Private.cpp
new file mode 100644
index 0000000..5fef241
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptIsolate_Private.cpp
@@ -0,0 +1,1751 @@
+#include "V8PCH.h"
+#include "Config.h"
+#include "MallocArrayBufferAllocator.h"
+#include "Translator.h"
+#include "ScopedArguments.h"
+#include "Exception.h"
+#include "Delegates.h"
+#include "JavascriptIsolate_Private.h"
+#include "JavascriptContext_Private.h"
+#include "Helpers.h"
+#include "JavascriptGeneratedClass.h"
+
+using namespace v8;
+
+#include "StructMemoryInstance.h"
+
+template <typename CppType>
+struct TStructReader
+{	
+	UScriptStruct* ScriptStruct;
+
+	TStructReader(UScriptStruct* InScriptStruct)
+		: ScriptStruct(InScriptStruct)
+	{}
+
+	bool Read(Isolate* isolate, Local<Value> Value, CppType& Target) const;
+};
+
+static v8::ArrayBuffer::Contents GCurrentContents;
+
+int32 FArrayBufferAccessor::GetSize()
+{
+	return GCurrentContents.ByteLength();
+}
+
+void* FArrayBufferAccessor::GetData()
+{
+	return GCurrentContents.Data();
+}
+
+void FArrayBufferAccessor::Discard()
+{
+	GCurrentContents = v8::ArrayBuffer::Contents();
+}
+
+class FJavascriptIsolateImplementation : public FJavascriptIsolate
+{
+public:
+	FJavascriptContext* GetContext()
+	{
+		return FJavascriptContext::FromV8(isolate_->GetCurrentContext());
+	}
+
+	Persistent<ObjectTemplate> GlobalTemplate;
+
+	// Allocator instance should be set for V8's ArrayBuffer's
+	FMallocArrayBufferAllocator AllocatorInstance;	
+
+	IDelegateManager* Delegates;
+
+	struct FObjectPropertyAccessors
+	{
+		static Local<Value> Get(Isolate* isolate, Local<Object> self, UProperty* Property)
+		{
+			auto Object = UObjectFromV8(self);
+
+			if (IsValid(Object))
+			{
+				if (auto p = Cast<UMulticastDelegateProperty>(Property))
+				{
+					return GetSelf(isolate)->Delegates->GetProxy(self, Object, p);
+				}
+				else if (auto p = Cast<UDelegateProperty>(Property))
+				{
+					return GetSelf(isolate)->Delegates->GetProxy(self, Object, p);
+				}
+				else
+				{
+					return ReadProperty(isolate, Property, (uint8*)Object, FObjectPropertyOwner(Object));
+				}
+			}
+			else
+			{
+				return Undefined(isolate);
+			}
+		}
+
+		//@TODO : Property-type 'routing' is not necessary!
+		static void Set(Isolate* isolate, Local<Object> self, UProperty* Property, Local<Value> value)
+		{
+			FIsolateHelper I(isolate);
+
+			auto Object = UObjectFromV8(self);
+
+			// Direct access to delegate
+			auto SetDelegate = [&](Local<Value> proxy) {
+				if (!proxy->IsObject())
+				{
+					I.Throw(TEXT("Set delegate on invalid instance"));
+					return;
+				}
+
+				if (!(value->IsFunction() || value->IsNull() || value->IsArray()))
+				{
+					I.Throw(TEXT("Only [function] or null allowed to set delegate"));
+					return;
+				}
+
+				auto ProxyObject = proxy->ToObject();
+				{					
+					auto clear_fn = Handle<Function>::Cast(ProxyObject->Get(I.Keyword("Clear")));
+					clear_fn->Call(ProxyObject, 0, nullptr);
+				}
+
+				auto add_fn = Handle<Function>::Cast(ProxyObject->Get(I.Keyword("Add")));
+
+				// "whole array" can be set
+				if (value->IsArray())
+				{
+					auto arr = Handle<Array>::Cast(value);
+					auto Length = arr->Length();					
+					for (decltype(Length) Index = 0; Index < Length; ++Index)
+					{						
+						auto elem = arr->Get(Index);
+						Handle<Value> args[] = {elem};
+						add_fn->Call(ProxyObject, 1, args);
+					}
+				}
+				// only one delegate
+				else if (!value->IsNull())
+				{					
+					Handle<Value> args[] = {value};
+					add_fn->Call(ProxyObject, 1, args);
+				}
+			};
+
+			if (IsValid(Object))
+			{
+				// Multicast delegate
+				if (auto p = Cast<UMulticastDelegateProperty>(Property))
+				{
+					auto proxy = GetSelf(isolate)->Delegates->GetProxy(self, Object, p);
+					SetDelegate(proxy);					
+				}
+				// delegate
+				else if (auto p = Cast<UDelegateProperty>(Property))
+				{
+					auto proxy = GetSelf(isolate)->Delegates->GetProxy(self, Object, p);
+					SetDelegate(proxy);
+				}
+				else
+				{
+					WriteProperty(isolate, Property, (uint8*)Object, value);
+				}				
+			}
+		}
+	};
+
+	struct FStructPropertyAccessors
+	{
+		static Local<Value> Get(Isolate* isolate, Local<Object> self, UProperty* Property)
+		{
+			auto Instance = FStructMemoryInstance::FromV8(self);
+			return ReadProperty(isolate, Property, Instance->GetMemory(), FStructMemoryPropertyOwner(Instance));
+		}
+
+		static void Set(Isolate* isolate, Local<Object> self, UProperty* Property, Local<Value> value)
+		{
+			auto Instance = FStructMemoryInstance::FromV8(self);
+			WriteProperty(isolate, Property, Instance->GetMemory(), value);
+		}
+	};
+
+	void RegisterSelf(Isolate* isolate)
+	{
+		isolate_ = isolate;
+		isolate->SetData(0, this);
+
+		Delegates = IDelegateManager::Create(isolate);
+	}		
+
+	FJavascriptIsolateImplementation()
+	{
+		Isolate::CreateParams params;
+
+		// Set our array buffer allocator instance
+		params.array_buffer_allocator = &AllocatorInstance;
+
+		// Bind this instance to newly created V8 isolate
+		RegisterSelf(Isolate::New(params));
+
+		GenerateBlueprintFunctionLibraryMapping();
+
+		InitializeGlobalTemplate();
+	}
+
+	void InitializeGlobalTemplate()
+	{
+		// Declares isolate/handle scope
+		Isolate::Scope isolate_scope(isolate_);
+		HandleScope handle_scope(isolate_);
+
+		// Create a new object template
+		auto ObjectTemplate = ObjectTemplate::New(isolate_);
+
+		// Save it into the persistant handle
+		GlobalTemplate.Reset(isolate_, ObjectTemplate);
+
+		// Export all structs
+		for (TObjectIterator<UScriptStruct> It; It; ++It)
+		{
+			ExportStruct(*It);
+		}						
+
+		// Export all classes
+		for (TObjectIterator<UClass> It; It; ++It)
+		{
+			ExportClass(*It);
+		}
+
+		ExportConsole(ObjectTemplate);
+
+		ExportMemory(ObjectTemplate);
+	}		
+
+	~FJavascriptIsolateImplementation()
+	{
+		ReleaseAllPersistentHandles();		
+
+		Delegates->Destroy();
+		Delegates = nullptr;
+
+		isolate_->Dispose();
+	}	
+
+	void ReleaseAllPersistentHandles()
+	{
+		// Release all exported classes
+		ClassToFunctionTemplateMap.Empty();
+
+		// Release all exported structs(non-class)
+		ScriptStructToFunctionTemplateMap.Empty();				
+
+		// Release global template
+		GlobalTemplate.Reset();
+	}
+
+	void GenerateBlueprintFunctionLibraryMapping()
+	{
+		for (TObjectIterator<UClass> It; It; ++It)
+		{
+			UClass* Class = *It;
+
+			// Blueprint function library only
+			if (Class->IsChildOf(UBlueprintFunctionLibrary::StaticClass()))
+			{
+				// Iterate over all functions
+				for (TFieldIterator<UFunction> FuncIt(Class, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
+				{
+					auto Function = *FuncIt;
+					TFieldIterator<UProperty> It(Function);
+
+					// It should be a static function and have first argument to bind with.
+					if ((Function->FunctionFlags & FUNC_Static) && It && (It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm)
+					{
+						// The first argument should be type of object
+						if (auto p = Cast<UObjectPropertyBase>(*It))
+						{
+							auto TargetClass = p->PropertyClass;
+
+							// GetWorld() may fail and crash, so target class is bound to UWorld
+							if (TargetClass == UObject::StaticClass() && (p->GetName() == TEXT("WorldContextObject") || p->GetName() == TEXT("WorldContext")))
+							{
+								TargetClass = UWorld::StaticClass();
+							}
+
+							BlueprintFunctionLibraryMapping.Add(TargetClass, Function);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	// To tell Unreal engine's GC not to destroy these objects!
+	virtual void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) override
+	{
+		// All classes
+		for (auto It = ClassToFunctionTemplateMap.CreateIterator(); It; ++It)
+		{
+			Collector.AddReferencedObject(It.Key(), InThis);
+		}
+
+		// All structs
+		for (auto It = ScriptStructToFunctionTemplateMap.CreateIterator(); It; ++It)
+		{
+			Collector.AddReferencedObject(It.Key(), InThis);
+		}
+	}	
+
+	Local<Value> InternalReadProperty(UProperty* Property, uint8* Buffer, const IPropertyOwner& Owner)
+	{
+		FIsolateHelper I(isolate_);
+
+		if (!Buffer)
+		{
+			I.Throw(TEXT("Read property from invalid memory"));
+			return Undefined(isolate_);
+		}
+
+		if (auto p = Cast<UIntProperty>(Property))
+		{
+			return Int32::New(isolate_, p->GetPropertyValue_InContainer(Buffer));
+		}
+		else if (auto p = Cast<UFloatProperty>(Property))
+		{
+			return Number::New(isolate_, p->GetPropertyValue_InContainer(Buffer));
+		}
+		else if (auto p = Cast<UBoolProperty>(Property))
+		{
+			return Boolean::New(isolate_, p->GetPropertyValue_InContainer(Buffer));
+		}
+		else if (auto p = Cast<UNameProperty>(Property))
+		{
+			auto name = p->GetPropertyValue_InContainer(Buffer);
+			return I.Keyword(name.ToString());
+		}
+		else if (auto p = Cast<UStrProperty>(Property))
+		{
+			const FString& Data = p->GetPropertyValue_InContainer(Buffer);
+			return V8_String(isolate_, Data);
+		}
+		else if (auto p = Cast<UTextProperty>(Property))
+		{
+			const FText& Data = p->GetPropertyValue_InContainer(Buffer);
+			return V8_String(isolate_, Data.ToString());
+		}		
+		else if (auto p = Cast<UClassProperty>(Property))
+		{			
+			auto Class = Cast<UClass>(p->GetPropertyValue_InContainer(Buffer));
+
+			if (Class)
+			{
+				ExportClass(Class);
+
+				auto context = isolate_->GetCurrentContext();
+				auto name = I.Keyword(FV8Config::Safeify(Class->GetName()));
+
+				return context->Global()->Get(name);
+			}
+			else
+			{
+				return Undefined(isolate_);
+			}
+		}
+		else if (auto p = Cast<UStructProperty>(Property))
+		{
+			if (auto ScriptStruct = Cast<UScriptStruct>(p->Struct))
+			{	
+				return ExportStructInstance(ScriptStruct, p->ContainerPtrToValuePtr<uint8>(Buffer), Owner);
+			}			
+			else
+			{
+				UE_LOG(Javascript, Warning, TEXT("Non ScriptStruct found : %s"), *p->Struct->GetName());
+				
+				return Undefined(isolate_);
+			}			
+		}		
+		else if (auto p = Cast<UArrayProperty>(Property))
+		{
+			FScriptArrayHelper_InContainer helper(p, Buffer);
+			auto len = (uint32_t)(helper.Num());
+			auto arr = Array::New(isolate_, len);
+			auto context = isolate_->GetCurrentContext();
+
+			for (decltype(len) Index = 0; Index < len; ++Index)
+			{	
+				arr->Set(context, Index, ReadProperty(isolate_, p->Inner, helper.GetRawPtr(Index), Owner));
+			}
+
+			return arr;
+		}
+		else if (auto p = Cast<UObjectPropertyBase>(Property))
+		{
+			return ExportObject(p->GetObjectPropertyValue_InContainer(Buffer));
+		}				
+		else if (auto p = Cast<UByteProperty>(Property))
+		{
+			auto Value = p->GetPropertyValue_InContainer(Buffer);
+
+			if (p->Enum)
+			{							
+				return I.Keyword(p->Enum->GetEnumName(Value));
+			}			
+			else
+			{
+				return Int32::New(isolate_, Value);
+			}
+		}
+		else
+		{
+			return I.Keyword("<Unsupported type>");
+		}
+	}
+
+	void ReadOffStruct(Local<Object> v8_obj, UStruct* Struct, uint8* struct_buffer)
+	{
+		FIsolateHelper I(isolate_);
+
+		for (TFieldIterator<UProperty> PropertyIt(Struct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
+		{
+			auto Property = *PropertyIt;
+			auto PropertyName = Property->GetFName();
+
+			auto name = I.Keyword(PropertyName.ToString());
+			auto value = v8_obj->Get(name);
+
+			if (!value.IsEmpty() && !value->IsUndefined())
+			{
+				InternalWriteProperty(Property, struct_buffer, value);
+			}
+		}
+	}	
+
+	void InternalWriteProperty(UProperty* Property, uint8* Buffer, Handle<Value> Value)
+	{
+		FIsolateHelper I(isolate_);
+
+		if (!Buffer)
+		{
+			I.Throw(TEXT("Write property on invalid memory"));
+			return;
+		}
+
+		if (Value.IsEmpty() || Value->IsUndefined()) return;
+
+		if (auto p = Cast<UIntProperty>(Property))
+		{
+			p->SetPropertyValue_InContainer(Buffer, Value->Int32Value());
+		}
+		else if (auto p = Cast<UFloatProperty>(Property))
+		{
+			p->SetPropertyValue_InContainer(Buffer, Value->NumberValue());
+		}
+		else if (auto p = Cast<UBoolProperty>(Property))
+		{
+			p->SetPropertyValue_InContainer(Buffer, Value->BooleanValue());
+		}
+		else if (auto p = Cast<UNameProperty>(Property))
+		{			
+			p->SetPropertyValue_InContainer(Buffer, FName(*StringFromV8(Value)));
+		}
+		else if (auto p = Cast<UStrProperty>(Property))
+		{
+			p->SetPropertyValue_InContainer(Buffer, StringFromV8(Value));
+		}		
+		else if (auto p = Cast<UTextProperty>(Property))
+		{
+			p->SetPropertyValue_InContainer(Buffer, FText::FromString(StringFromV8(Value)));
+		}
+		else if (auto p = Cast<UClassProperty>(Property))
+		{
+			p->SetPropertyValue_InContainer(Buffer, UClassFromV8(isolate_, Value));
+		}
+		else if (auto p = Cast<UStructProperty>(Property))
+		{
+			if (auto ScriptStruct = Cast<UScriptStruct>(p->Struct))
+			{
+				auto Instance = FStructMemoryInstance::FromV8(Value);
+
+				// If given value is an instance
+				if (Instance)
+				{
+					auto GivenStruct = Instance->Struct;
+
+					// Type-checking needed
+					if (GivenStruct == ScriptStruct)
+					{
+						p->CopyCompleteValue(p->ContainerPtrToValuePtr<void>(Buffer), Instance->GetMemory());
+					}
+					else
+					{				
+						I.Throw(FString::Printf(TEXT("Wrong struct type (given:%s), (expected:%s)"), *GivenStruct->GetName(), *p->Struct->GetName()));
+					}
+				}
+				// If raw javascript object has been passed,
+				else if (Value->IsObject())
+				{
+					auto v8_obj = Value->ToObject();
+					auto struct_buffer = p->ContainerPtrToValuePtr<uint8>(Buffer);
+
+					auto Struct = p->Struct;
+
+					ReadOffStruct(v8_obj, Struct, struct_buffer);
+				}
+				else
+				{
+					I.Throw(TEXT("Needed struct data"));
+				}
+			}
+			else
+			{
+				I.Throw(FString::Printf(TEXT("No ScriptStruct found : %s"), *p->Struct->GetName()));
+			}					
+		}		
+		else if (auto p = Cast<UArrayProperty>(Property))
+		{
+			if (Value->IsArray())
+			{
+				auto arr = Handle<Array>::Cast(Value);
+				auto len = arr->Length();
+
+				FScriptArrayHelper_InContainer helper(p, Buffer);
+
+				// synchronize the length
+				auto CurSize = (uint32_t)helper.Num();
+				if (CurSize < len)
+				{
+					helper.AddValues(len - CurSize);
+				}
+				else if (CurSize > len)
+				{
+					helper.RemoveValues(len, CurSize - len);
+				}
+
+				for (decltype(len) Index = 0; Index < len; ++Index)
+				{
+					WriteProperty(isolate_, p->Inner, helper.GetRawPtr(Index), arr->Get(Index));
+				}
+			}
+			else
+			{
+				I.Throw(TEXT("Should write into array by passing an array instance"));
+			}
+		}
+		else if (auto p = Cast<UByteProperty>(Property))
+		{
+			if (p->Enum)
+			{
+				auto Str = StringFromV8(Value);
+				auto EnumValue = p->Enum->FindEnumIndex(FName(*Str));
+				if (EnumValue == INDEX_NONE)
+				{
+					I.Throw(FString::Printf(TEXT("Enum Text %s for Enum %s failed to resolve to any value"), *Str, *p->Enum->GetName()));
+				}
+				else
+				{
+					p->SetPropertyValue_InContainer(Buffer, EnumValue);
+				}
+			}			
+			else
+			{				
+				p->SetPropertyValue_InContainer(Buffer, Value->Int32Value());
+			}
+		}
+		else if (auto p = Cast<UObjectPropertyBase>(Property))
+		{
+			p->SetObjectPropertyValue_InContainer(Buffer, UObjectFromV8(Value));
+		}
+	};		
+	
+	virtual Local<ObjectTemplate> GetGlobalTemplate() override
+	{
+		return Local<ObjectTemplate>::New(isolate_, GlobalTemplate);
+	}
+
+	void ExportConsole(Local<ObjectTemplate> global_templ)
+	{
+		FIsolateHelper I(isolate_);
+
+		Handle<Context> context = Context::New(isolate_);
+		Context::Scope ContextScope(context);
+
+		Local<FunctionTemplate> Template = I.FunctionTemplate();
+
+		auto add_fn = [&](const char* name, FunctionCallback fn) {			
+			Template->PrototypeTemplate()->Set(I.Keyword(name), I.FunctionTemplate(fn));
+		};
+
+		// console.log
+		add_fn("log", [](const FunctionCallbackInfo<Value>& info)
+		{
+			UE_LOG(Javascript, Log, TEXT("%s"), *StringFromArgs(info));
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// console.warn
+		add_fn("warn", [](const FunctionCallbackInfo<Value>& info)
+		{
+			UE_LOG(Javascript, Warning, TEXT("%s"), *StringFromArgs(info));
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// console.info
+		add_fn("info", [](const FunctionCallbackInfo<Value>& info)
+		{
+			UE_LOG(Javascript, Display, TEXT("%s"), *StringFromArgs(info));
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// console.error
+		add_fn("error", [](const FunctionCallbackInfo<Value>& info)
+		{
+			UE_LOG(Javascript, Error, TEXT("%s"), *StringFromArgs(info));
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// console.assert
+		add_fn("assert", [](const FunctionCallbackInfo<Value>& info)
+		{
+			bool to_assert = info.Length() < 1 || info[0]->IsFalse();
+			if (to_assert)
+			{
+				auto stack_frame = StackTrace::CurrentStackTrace(info.GetIsolate(), 1, StackTrace::kOverview)->GetFrame(0);
+				auto filename = stack_frame->GetScriptName();
+				auto line_number = stack_frame->GetLineNumber();
+
+				UE_LOG(Javascript, Error, TEXT("Assertion:%s:%d %s"), *StringFromV8(filename), line_number, *StringFromArgs(info, 1));
+			}
+
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// console.void
+		add_fn("void", [](const FunctionCallbackInfo<Value>& info)
+		{
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		global_templ->Set(
+			I.Keyword("console"), 
+			// Create an instance
+			Template->GetFunction()->NewInstance(), 
+			// Do not modify!
+			ReadOnly);
+	}	
+
+	void ExportMemory(Local<ObjectTemplate> global_templ)
+	{
+		FIsolateHelper I(isolate_);
+
+		Handle<Context> context = Context::New(isolate_);
+		Context::Scope ContextScope(context);
+
+		Local<FunctionTemplate> Template = I.FunctionTemplate();
+
+		auto add_fn = [&](const char* name, FunctionCallback fn) {
+			Template->PrototypeTemplate()->Set(I.Keyword(name), I.FunctionTemplate(fn));
+		};		
+
+		// memory.bind
+		add_fn("bind", [](const FunctionCallbackInfo<Value>& info)
+		{
+			FIsolateHelper I(info.GetIsolate());
+
+			if (info.Length() == 1 && info[0]->IsArrayBuffer())
+			{
+				auto arr = info[0].As<ArrayBuffer>();
+
+				GCurrentContents = arr->Externalize();
+			}
+			else
+			{
+				GCurrentContents = v8::ArrayBuffer::Contents();
+			}
+
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// memory.unbind
+		add_fn("unbind", [](const FunctionCallbackInfo<Value>& info)
+		{
+			FIsolateHelper I(info.GetIsolate());
+
+			GCurrentContents = v8::ArrayBuffer::Contents();
+			
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		// console.void
+		add_fn("write", [](const FunctionCallbackInfo<Value>& info)
+		{
+			FIsolateHelper I(info.GetIsolate());
+
+			if (info.Length() == 2)
+			{
+				auto filename = info[0];
+				auto data = info[1];
+
+				FArchive* Ar = IFileManager::Get().CreateFileWriter(*StringFromV8(info[0]), 0);
+				if (Ar)
+				{
+					if (data->IsArrayBuffer())
+					{
+						auto arr = data.As<ArrayBuffer>();
+						auto Contents = arr->Externalize();
+
+						Ar->Serialize(Contents.Data(), Contents.ByteLength());
+					}
+
+					delete Ar;
+				}
+			}
+			else
+			{
+				I.Throw(TEXT("Two arguments needed"));
+			}
+
+			info.GetReturnValue().Set(info.Holder());
+		});
+
+		global_templ->Set(
+			I.Keyword("memory"),
+			// Create an instance
+			Template->GetFunction()->NewInstance(),
+			// Do not modify!
+			ReadOnly);
+	}
+	
+	template <typename Fn>
+	static Local<Value> CallFunction(Isolate* isolate, Local<Value> self, UFunction* Function, UObject* Object, Fn&& GetArg) 
+	{
+		FIsolateHelper I(isolate);
+
+		EscapableHandleScope handle_scope(isolate);
+
+		// Allocate buffer(param size) in stack
+		uint8* Buffer = (uint8*)FMemory_Alloca(Function->ParmsSize);
+
+		// Arguments should construct and destruct along this scope
+		FScopedArguments scoped_arguments(Function, Buffer);		
+
+		// Does this function return some parameters by reference?
+		bool bHasAnyOutParams = false;
+
+		// Argument index
+		int ArgIndex = 0;
+
+		// Intentionally declares iterator outside for-loop scope
+		TFieldIterator<UProperty> It(Function);
+
+		// Iterate over input parameters
+		for (; It && (It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++It)
+		{
+			auto Prop = *It;
+
+			// Get argument from caller
+			auto arg = GetArg(ArgIndex++);
+
+			// Do we have valid argument?
+			if (!arg.IsEmpty() && !arg->IsUndefined())
+			{				
+				WriteProperty(isolate, Prop, Buffer, arg);
+			}							
+
+			// This is 'out ref'!
+			if ((It->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+			{
+				bHasAnyOutParams = true;
+			}
+		}
+
+		FFrame Stack(Object, Function, Buffer, nullptr, Function->Children);
+
+		int32 FunctionCallspace = Object->GetFunctionCallspace(Function, Buffer, &Stack);
+
+		if (FunctionCallspace & FunctionCallspace::Remote)
+		{
+			Object->CallRemoteFunction(Function, Buffer, Stack.OutParms, &Stack);
+		}
+		
+		if (FunctionCallspace & FunctionCallspace::Local)
+		{
+			// Call regular native function.
+			FScopeCycleCounterUObject ContextScope(Object);
+			FScopeCycleCounterUObject FunctionScope(Function);
+			
+			Object->ProcessEvent(Function, Buffer);
+		}
+
+		// In case of 'out ref'
+		if (bHasAnyOutParams)
+		{
+			// Allocate an object to pass return values within
+			auto OutParameters = Object::New(isolate);
+
+			// Iterate over parameters again
+			for (TFieldIterator<UProperty> It(Function); It; ++It)
+			{
+				UProperty* Param = *It;
+				
+				auto PropertyFlags = Param->GetPropertyFlags();
+
+				// pass return parameter as '$'
+				if (PropertyFlags & CPF_ReturnParm)
+				{
+					// value can be null if isolate is in trouble
+					auto value = ReadProperty(isolate, Param, Buffer, FNoPropertyOwner());
+					if (!value.IsEmpty())
+					{
+						OutParameters->Set(
+							// "$"
+							I.Keyword("$"),
+							// property value
+							value
+							);
+					}
+				}
+				// rejects 'const T&' and pass 'T&' as its name
+				else if ((PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+				{
+					// value can be null if isolate is in trouble
+					auto value = ReadProperty(isolate, Param, Buffer, FNoPropertyOwner());
+					if (!value.IsEmpty())
+					{
+						OutParameters->Set(
+							// parameter name
+							I.Keyword(Param->GetName()),
+							// property value
+							value
+							);
+					}
+				}
+			}
+
+			// We're done
+			return handle_scope.Escape(OutParameters);
+		}
+		else
+		{
+			// Iterate to fill out return parameter (if we have one)
+			for (; It; ++It)
+			{
+				UProperty* Param = *It;
+				if (Param->GetPropertyFlags() & CPF_ReturnParm)
+				{
+					return handle_scope.Escape(ReadProperty(isolate, Param, Buffer, FNoPropertyOwner()));
+				}
+			}
+		}		
+
+		// No return value available
+		return handle_scope.Escape(Undefined(isolate));
+	}
+		
+	void ExportFunction(Handle<FunctionTemplate> Template, UFunction* FunctionToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		// Exposed function body (it doesn't capture anything)
+		auto FunctionBody = [](const FunctionCallbackInfo<Value>& info)
+		{
+			auto isolate = info.GetIsolate();
+
+			FIsolateHelper I(isolate);
+
+			auto self = info.Holder();
+
+			// Retrieve "FUNCTION"
+			auto Function = reinterpret_cast<UFunction*>((Local<External>::Cast(info.Data()))->Value());
+
+			// Determine 'this'
+			auto Object = (Function->FunctionFlags & FUNC_Static) ? Function->GetOwnerClass()->ClassDefaultObject : UObjectFromV8(self);
+
+			// Check 'this' is valid
+			if (!IsValid(Object))
+			{
+				I.Throw(FString::Printf(TEXT("Invalid instance for calling a function %s"), *Function->GetName()));
+				return;
+			}
+			
+			info.GetReturnValue().Set(
+				// Call unreal engine function!
+				CallFunction(isolate, self, Function, Object, [&](int ArgIndex) -> Local<Value> {
+					// pass an argument if we have
+					if (ArgIndex < info.Length())
+					{
+						return info[ArgIndex];
+					}
+					// Otherwise, just return undefined.
+					else
+					{
+						return Undefined(isolate);
+					}
+				})
+			);
+		};
+
+		auto function_name = I.Keyword(FV8Config::Safeify(FunctionToExport->GetName()));
+		auto function = I.FunctionTemplate(FunctionBody, FunctionToExport);
+
+		// In case of static function, you can also call this function by 'Class.Method()'.
+		if (FunctionToExport->FunctionFlags & FUNC_Static)
+		{
+			Template->Set(function_name, function);
+		}
+
+		// Register the function to prototype template
+		Template->PrototypeTemplate()->Set(function_name, function);
+	}
+
+	void ExportBlueprintLibraryFunction(Handle<FunctionTemplate> Template, UFunction* FunctionToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		// Exposed function body (it doesn't capture anything)
+		auto FunctionBody = [](const FunctionCallbackInfo<Value>& info)
+		{
+			auto isolate = info.GetIsolate();
+
+			auto self = info.Holder();
+
+			// Retrieve "FUNCTION"
+			auto Function = reinterpret_cast<UFunction*>((Local<External>::Cast(info.Data()))->Value());
+
+			// 'this' should be CDO of owner class
+			auto Object = Function->GetOwnerClass()->ClassDefaultObject;						
+
+			info.GetReturnValue().Set(
+				// Call unreal engine function!
+				CallFunction(isolate, self, Function, Object, [&](int ArgIndex) -> Local<Value> {
+					// The first argument is bound automatically
+					if (ArgIndex == 0) 
+					{
+						return self;
+					}
+					// The rest arguments are being passed like normal function call.
+					else if (ArgIndex - 1 < info.Length())
+					{
+						return info[ArgIndex - 1];
+					}					
+					else
+					{
+						return Undefined(isolate);
+					}
+				})
+			);
+		};
+
+		auto function_name = I.Keyword(FV8Config::Safeify(FunctionToExport->GetName()));
+		auto function = I.FunctionTemplate(FunctionBody, FunctionToExport);
+		
+		// Register the function to prototype template
+		Template->PrototypeTemplate()->Set(function_name, function);		
+	}	
+	
+	template <typename PropertyAccessors>
+	void ExportProperty(Handle<FunctionTemplate> Template, UProperty* PropertyToExport, int32 PropertyIndex) 
+	{
+		FIsolateHelper I(isolate_);
+
+		// Property getter
+		auto Getter = [](Local<String> property, const PropertyCallbackInfo<Value>& info) {
+			auto isolate = info.GetIsolate();
+
+			auto data = info.Data();
+			check(data->IsExternal());
+
+			auto Property = reinterpret_cast<UProperty*>((Local<External>::Cast(data))->Value());
+			info.GetReturnValue().Set(PropertyAccessors::Get(isolate, info.This(), Property));			
+		};
+
+		// Property setter
+		auto Setter = [](Local<String> property, Local<Value> value, const PropertyCallbackInfo<void>& info) {
+			auto isolate = info.GetIsolate();
+
+			auto data = info.Data();
+			check(data->IsExternal())			
+
+			auto Property = reinterpret_cast<UProperty*>((Local<External>::Cast(data))->Value());
+			PropertyAccessors::Set(isolate, info.This(), Property, value);			
+		};
+
+		Template->PrototypeTemplate()->SetAccessor(
+			I.Keyword(FV8Config::Safeify(PropertyToExport->GetName())),
+			Getter, 
+			Setter, 
+			I.External(PropertyToExport),
+			DEFAULT,
+			(PropertyAttribute)(DontDelete | (FV8Config::IsWriteDisabledProperty(PropertyToExport) ? ReadOnly : 0))
+			);
+	}
+
+	void ExportHelperFunctions(UClass* ClassToExport, Local<FunctionTemplate> Template)
+	{
+		// Bind blue print library!
+		TArray<UFunction*> Functions;
+		BlueprintFunctionLibraryMapping.MultiFind(ClassToExport, Functions);
+
+		for (auto Function : Functions)
+		{
+			ExportBlueprintLibraryFunction(Template, Function);			
+		}
+	}
+
+	void AddMemberFunction_Class_GetClassObject(Local<FunctionTemplate> Template, UStruct* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			info.GetReturnValue().Set(GetSelf(isolate)->ForceExportObject(ClassToExport));
+		};
+
+		Template->Set(I.Keyword("GetClassObject"), I.FunctionTemplate(fn, ClassToExport));
+	}	
+
+	void AddMemberFunction_Class_CreateDefaultSubobject(Local<FunctionTemplate> Template, UStruct* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto isolate = info.GetIsolate();
+			HandleScope scope(isolate);
+
+			FIsolateHelper I(isolate);
+
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto ObjectInitializer = GetSelf(isolate)->GetContext()->GetObjectInitializer();
+
+			if (!ObjectInitializer)
+			{
+				I.Throw(TEXT("CreateDefaultSubobject must be called within ctor"));
+				return;
+			}
+
+			if (info.Length() < 1)
+			{
+				I.Throw(TEXT("Missing arg"));
+				return;
+			}
+			
+			auto Class = static_cast<UJavascriptGeneratedClass*>(ObjectInitializer->GetClass());
+			if (!Class->JavascriptContext.IsValid())
+			{
+				I.Throw(TEXT("Fatal"));
+				return;
+			}
+
+			auto Context = Class->JavascriptContext.Pin();
+			auto ReturnType = ClassToExport;
+			auto Name = StringFromV8(info[0]);
+			bool bTransient = info.Length() > 1 ? info[1]->BooleanValue() : false;
+			bool bIsRequired = info.Length() > 2 ? info[2]->BooleanValue() : true;
+			bool bIsAbstract = info.Length() > 3 ? info[3]->BooleanValue() : false;
+			auto Object = ObjectInitializer->CreateDefaultSubobject(ObjectInitializer->GetObj(), *Name, ReturnType, ReturnType, bIsRequired, bIsAbstract, bTransient);
+
+			info.GetReturnValue().Set(Context->ExportObject(Object));
+		};
+
+		Template->Set(I.Keyword("CreateDefaultSubobject"), I.FunctionTemplate(fn, ClassToExport));
+	}
+
+	void AddMemberFunction_Class_GetDefaultSubobjectByName(Local<FunctionTemplate> Template, UStruct* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			auto Name = StringFromV8(info[0]);
+
+			info.GetReturnValue().Set(GetSelf(isolate)->ExportObject(ClassToExport->GetDefaultSubobjectByName(*Name)));
+		};
+
+		Template->Set(I.Keyword("GetDefaultSubobjectByName"), I.FunctionTemplate(fn, ClassToExport));
+	}
+
+	void AddMemberFunction_Class_GetDefaultObject(Local<FunctionTemplate> Template, UStruct* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			info.GetReturnValue().Set(GetSelf(isolate)->ExportObject(ClassToExport->GetDefaultObject()));
+		};
+
+		Template->Set(I.Keyword("GetDefaultObject"), I.FunctionTemplate(fn, ClassToExport));
+	}
+
+	void AddMemberFunction_Class_Load(Local<FunctionTemplate> Template, UClass* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			FIsolateHelper I(isolate);
+
+			if (info.Length() == 1 && info[0]->IsString())
+			{
+				auto obj = StaticLoadObject(ClassToExport, nullptr, *StringFromV8(info[0]->ToString()));
+				auto out = GetSelf(isolate)->ExportObject(obj);
+				info.GetReturnValue().Set(out);
+			}
+			else
+			{
+				I.Throw(TEXT("Missing resource name to load"));
+			}
+		};
+
+		Template->Set(I.Keyword("Load"), I.FunctionTemplate(fn, ClassToExport));
+	}
+
+	void AddMemberFunction_Struct_C(Local<FunctionTemplate> Template, UStruct* StructToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			if (info.Length() == 1)
+			{
+				info.GetReturnValue().Set(info[0]);
+			}
+		};
+
+		Template->Set(I.Keyword("C"), I.FunctionTemplate(fn, StructToExport));		
+	}
+
+	void AddMemberFunction_Struct_clone(Local<FunctionTemplate> Template, UStruct* StructToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto Struct = reinterpret_cast<UScriptStruct*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			auto self = info.This();
+			auto out = Object::New(isolate);
+
+			auto Instance = FStructMemoryInstance::FromV8(self);
+
+			if (Instance->GetMemory())
+			{
+				info.GetReturnValue().Set(GetSelf(isolate)->ExportStructInstance(Instance->Struct, Instance->GetMemory(), FNoPropertyOwner()));
+			}
+		};
+
+		Template->PrototypeTemplate()->Set(I.Keyword("clone"), I.FunctionTemplate(fn, StructToExport));		
+	}
+
+	template <typename PropertyAccessor>
+	void AddMemberFunction_Struct_toJSON(Local<FunctionTemplate> Template, UStruct* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+		
+		auto fn = [](const FunctionCallbackInfo<Value>& info) {
+			auto Class = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+			FIsolateHelper I(isolate);
+
+			auto self = info.This();
+			auto out = Object::New(isolate);
+
+			auto Instance = FStructMemoryInstance::FromV8(self);
+
+			auto Object_toJSON = [&](Local<Value> value) -> Local<Value>
+			{
+				auto Object = UObjectFromV8(value);
+				if (Object == nullptr)
+				{
+					return Null(isolate);
+				}
+				else
+				{
+					return V8_String(isolate, Object->GetName());
+				}
+			};
+
+			for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
+			{
+				auto Property = *PropertyIt;
+
+				if (FV8Config::CanExportProperty(Class, Property))
+				{
+					auto PropertyName = Property->GetFName();
+
+					auto name = I.Keyword(PropertyName.ToString());
+					auto value = PropertyAccessor::Get(isolate, self, Property);
+					if (auto p = Cast<UObjectPropertyBase>(Property))
+					{
+						out->Set(name, Object_toJSON(value));						
+					}
+					else if (auto p = Cast<UArrayProperty>(Property))
+					{
+						if (auto q = Cast<UObjectPropertyBase>(p->Inner))
+						{
+							auto arr = Handle<Array>::Cast(value);
+							auto len = arr->Length();
+							
+							auto out_arr = Array::New(isolate, len);
+							out->Set(name, out_arr);
+
+							for (decltype(len) Index = 0; Index < len; ++Index)
+							{
+								out_arr->Set(Index, Object_toJSON(arr->Get(Index)));								
+							}															
+						}
+						else
+						{
+							out->Set(name, value);
+						}						
+					}
+					else
+					{
+						out->Set(name, value);
+					}
+				}
+			}
+
+			info.GetReturnValue().Set(out);
+		};
+
+		Template->PrototypeTemplate()->Set(I.Keyword("toJSON"), I.FunctionTemplate(fn, ClassToExport));		
+	}
+
+	Local<FunctionTemplate> InternalExportClass(UClass* ClassToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		EscapableHandleScope handle_scope(isolate_);
+
+		auto ConstructorBody = [](const FunctionCallbackInfo<Value>& info)
+		{
+			auto isolate = info.GetIsolate();
+
+			FIsolateHelper I(isolate);
+
+			auto ClassToExport = reinterpret_cast<UClass*>((Local<External>::Cast(info.Data()))->Value());			
+
+			auto self = info.This();
+
+			UObject* Associated = nullptr;
+
+			// Called by system (via ExportObject)
+			if (info.Length() == 1 && info[0]->IsExternal())
+			{				
+				auto ext = Local<External>::Cast(info[0]);
+
+				Associated = reinterpret_cast<UObject*>(ext->Value());				
+
+				if (!Associated->IsValidLowLevel())
+				{
+					Associated = nullptr;
+				}
+			}						
+
+			// Called by user (via 'new' operator)
+			if (!Associated)
+			{
+				// Custom constructors
+				if (ClassToExport->IsChildOf(AActor::StaticClass()))
+				{
+					if (info.Length() > 0)
+					{
+						auto World = Cast<UWorld>(UObjectFromV8(info[0]));
+						if (!World)
+						{
+							I.Throw(TEXT("Missing world to spawn"));
+							return;
+						}						
+						
+						FVector Location(ForceInitToZero);
+						FRotator Rotation(ForceInitToZero);
+						
+						UPackage* CoreUObjectPackage = UObject::StaticClass()->GetOutermost();
+						static UScriptStruct* VectorStruct = FindObjectChecked<UScriptStruct>(CoreUObjectPackage, TEXT("Vector"));
+						static UScriptStruct* RotatorStruct = FindObjectChecked<UScriptStruct>(CoreUObjectPackage, TEXT("Rotator"));
+						static TStructReader<FVector> VectorReader(VectorStruct);
+						static TStructReader<FRotator> RotatorReader(RotatorStruct);
+
+						if (info.Length() > 1)
+						{
+							if (!VectorReader.Read(isolate, info[1], Location)) return;							
+
+							if (info.Length() > 2)
+							{
+								if (!RotatorReader.Read(isolate, info[2], Rotation)) return;								
+							}
+						}
+
+						const bool bIsJavascriptClass = ClassToExport->GetClass()->IsChildOf(UJavascriptGeneratedClass::StaticClass());
+						if (bIsJavascriptClass)
+						{
+							GetSelf(isolate)->ObjectUnderConstructionStack.Push(FPendingClassConstruction(self, ClassToExport));
+						}
+						
+						Associated = World->SpawnActor(ClassToExport, &Location, &Rotation);
+
+						if (bIsJavascriptClass)
+						{
+							const auto& Last = GetSelf(isolate)->ObjectUnderConstructionStack.Last();
+
+							bool bSafeToQuit = Last.bCatched;							
+
+							GetSelf(isolate)->ObjectUnderConstructionStack.Pop();
+
+							if (bSafeToQuit)
+							{
+								return;
+							}
+						}
+
+						if (!Associated)
+						{
+							I.Throw(TEXT("Failed to spawn"));
+							return;
+						}						
+					}					
+				}
+				else
+				{
+					UObject* Outer = GetTransientPackage();
+					FName Name = NAME_None;
+
+					if (info.Length() > 0)
+					{
+						if (auto value = UObjectFromV8(info[0]))
+						{
+							Outer = value;
+						}
+					}
+
+					Associated = NewObject<UObject>(Outer, ClassToExport, Name);
+				}				
+			}			
+
+			if (Associated)
+			{
+				FPendingClassConstruction(self, ClassToExport).Finalize(GetSelf(isolate), Associated);				
+			}			
+		};
+		
+		auto Template = I.FunctionTemplate(ConstructorBody, ClassToExport);
+		Template->InstanceTemplate()->SetInternalFieldCount(1);
+		
+		AddMemberFunction_Struct_C(Template, ClassToExport);
+
+		// load
+		if (!ClassToExport->IsChildOf(AActor::StaticClass()))
+		{
+			AddMemberFunction_Class_Load(Template, ClassToExport);			
+		}
+
+		AddMemberFunction_Class_GetClassObject(Template, ClassToExport);
+		AddMemberFunction_Class_CreateDefaultSubobject(Template, ClassToExport);
+
+		AddMemberFunction_Class_GetDefaultObject(Template, ClassToExport);
+		AddMemberFunction_Class_GetDefaultSubobjectByName(Template, ClassToExport);
+		
+		AddMemberFunction_Struct_toJSON<FObjectPropertyAccessors>(Template, ClassToExport);
+
+		Template->SetClassName(I.Keyword(ClassToExport->GetName()));
+
+		auto static_class = I.Keyword("StaticClass");
+
+		// access thru Class.prototype.StaticClass
+		Template->PrototypeTemplate()->Set(static_class, I.External(ClassToExport));
+		Template->Set(static_class, I.External(ClassToExport));		
+
+		for (TFieldIterator<UFunction> FuncIt(ClassToExport, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
+		{
+			UFunction* Function = *FuncIt;
+			if (FV8Config::CanExportFunction(ClassToExport, Function))
+			{
+				ExportFunction(Template, Function);
+			}
+		}		
+
+		int32 PropertyIndex = 0;
+		for (TFieldIterator<UProperty> PropertyIt(ClassToExport, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt, ++PropertyIndex)
+		{
+			UProperty* Property = *PropertyIt;
+			if (FV8Config::CanExportProperty(ClassToExport, Property))
+			{
+				ExportProperty<FObjectPropertyAccessors>(Template, Property, PropertyIndex);
+			}
+		}
+
+		return handle_scope.Escape(Template);
+	}	
+
+	Local<FunctionTemplate> InternalExportStruct(UScriptStruct* StructToExport)
+	{
+		FIsolateHelper I(isolate_);
+
+		EscapableHandleScope handle_scope(isolate_);
+
+		auto fn = [](const FunctionCallbackInfo<Value>& info)
+		{
+			auto StructToExport = reinterpret_cast<UScriptStruct*>((Local<External>::Cast(info.Data()))->Value());
+
+			auto isolate = info.GetIsolate();
+
+			FIsolateHelper I(isolate);
+
+			auto self = info.This();
+
+			TSharedPtr<FStructMemoryInstance> Memory;
+			
+			if (info.Length() == 2 && info[0]->IsExternal() && info[1]->IsExternal())
+			{			
+				IPropertyOwner& Owner = *reinterpret_cast<IPropertyOwner*>(Local<External>::Cast(info[1])->Value());
+
+				Memory = FStructMemoryInstance::Create(StructToExport, Owner, Local<External>::Cast(info[0])->Value());
+			}
+			else
+			{
+				Memory = FStructMemoryInstance::Create(StructToExport, FNoPropertyOwner());
+			}
+			
+			GetSelf(isolate)->RegisterScriptStructInstance(Memory, self);
+
+			self->SetAlignedPointerInInternalField(0, Memory.Get());			
+		};
+				
+		auto Template = I.FunctionTemplate(fn, StructToExport);
+		Template->InstanceTemplate()->SetInternalFieldCount(1);
+
+		AddMemberFunction_Struct_C(Template, StructToExport);
+		AddMemberFunction_Struct_clone(Template, StructToExport);
+		AddMemberFunction_Struct_toJSON<FStructPropertyAccessors>(Template, StructToExport);
+
+		Template->SetClassName(I.Keyword(StructToExport->GetName()));
+
+		auto static_class = I.Keyword("StaticClass");
+
+		// access thru Class.prototype.StaticClass
+		Template->PrototypeTemplate()->Set(static_class, I.External(StructToExport));
+		Template->Set(static_class, I.External(StructToExport));
+
+		int32 PropertyIndex = 0;
+		for (TFieldIterator<UProperty> PropertyIt(StructToExport, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt, ++PropertyIndex)
+		{
+			UProperty* Property = *PropertyIt;
+			if (FV8Config::CanExportProperty(StructToExport, Property))
+			{
+				ExportProperty<FStructPropertyAccessors>(Template, Property, PropertyIndex);
+			}
+		}
+
+		return handle_scope.Escape(Template);
+	}
+	
+	Local<FunctionTemplate> ExportStruct(UScriptStruct* ScriptStruct)
+	{
+		auto ExportedFunctionTemplatePtr = ScriptStructToFunctionTemplateMap.Find(ScriptStruct);
+		if (ExportedFunctionTemplatePtr == nullptr)
+		{				
+			auto Template = InternalExportStruct(ScriptStruct);
+
+			auto SuperStruct = Cast<UScriptStruct>(ScriptStruct->GetSuperStruct());
+			if (SuperStruct)
+			{
+				Template->Inherit(ExportStruct(SuperStruct));
+			}				
+
+			RegisterStruct(ScriptStructToFunctionTemplateMap, ScriptStruct, Template);
+
+			return Template;
+		}
+		else
+		{
+			return Local<FunctionTemplate>::New(isolate_, *ExportedFunctionTemplatePtr);
+		}
+	}	
+
+	virtual Local<FunctionTemplate> ExportClass(UClass* Class, bool bAutoRegister = true) override
+	{
+		auto ExportedFunctionTemplatePtr = ClassToFunctionTemplateMap.Find(Class);
+		if (ExportedFunctionTemplatePtr == nullptr)
+		{
+			auto Template = InternalExportClass(Class);
+
+			auto SuperClass = Class->GetSuperClass();
+			if (SuperClass)
+			{
+				Template->Inherit(ExportClass(SuperClass));
+			}
+
+			ExportHelperFunctions(Class, Template);
+
+			if (bAutoRegister)
+			{
+				RegisterClass(Class, Template);
+			}			
+			
+			return Template;
+		}
+		else
+		{
+			return Local<FunctionTemplate>::New(isolate_, *ExportedFunctionTemplatePtr);
+		}
+	}
+
+	Local<Value> ExportStructInstance(UScriptStruct* Struct, uint8* Buffer, const IPropertyOwner& Owner)
+	{
+		FIsolateHelper I(isolate_);
+		if (!Struct || !Buffer)
+		{
+			return Undefined(isolate_);
+		}
+
+		auto v8_struct = ExportStruct(Struct);
+		auto arg = I.External(Buffer);
+		auto arg2 = I.External((void*)&Owner);
+		Handle<Value> args[] = { arg, arg2 };
+
+		return v8_struct->GetFunction()->NewInstance(2, args);
+	}
+
+	Local<Value> ForceExportObject(UObject* Object)
+	{
+		FIsolateHelper I(isolate_);
+		if (!Object)
+		{
+			return Undefined(isolate_);
+		}
+
+		auto ObjectPtr = GetContext()->ObjectToObjectMap.Find(Object);
+		if (ObjectPtr == nullptr)
+		{
+			Local<Value> value;
+
+			auto v8_class = ExportClass(Object->GetClass());
+			auto arg = I.External(Object);
+			Handle<Value> args[] = { arg };
+
+			value = v8_class->GetFunction()->NewInstance(1, args);
+
+			return value;
+		}
+		else
+		{
+			return Local<Value>::New(isolate_, *ObjectPtr);
+		}
+	}
+
+	Local<Value> ExportObject(UObject* Object, bool bForce = false) override
+	{
+		if (bForce) return ForceExportObject(Object);
+
+		FIsolateHelper I(isolate_);
+		if (!Object)
+		{
+			return Undefined(isolate_);
+		}
+
+		auto Context = GetContext();
+		if (!Context)
+		{
+			return Undefined(isolate_);
+		}
+
+		auto ObjectPtr = Context->ObjectToObjectMap.Find(Object);
+		if (ObjectPtr == nullptr)
+		{
+			if (ObjectUnderConstructionStack.Num() > 0)
+			{
+				auto& Last = ObjectUnderConstructionStack.Last();
+				if (!Last.bCatched)
+				{
+					if (!Object->HasAnyFlags(RF_ClassDefaultObject) && Object->IsA(Last.Class))
+					{
+						Last.bCatched = true;
+						Last.Finalize(this, Object);
+						return Last.Object;
+					}					
+				}
+			}
+			Local<Value> value;
+
+			auto Class = Cast<UClass>(Object);
+			if (Class)
+			{
+				value = ExportClass(Class)->GetFunction();
+			}
+			else
+			{
+				auto v8_class = ExportClass(Object->GetClass());
+				auto arg = I.External(Object);
+				Handle<Value> args[] = { arg };
+
+				value = v8_class->GetFunction()->NewInstance(1, args);
+			}
+
+			return value;
+		}
+		else
+		{
+			return Local<Value>::New(isolate_, *ObjectPtr);
+		}
+	}
+
+	// For tracking exported entities
+	template <typename U, typename T>
+	void SetWeak(UniquePersistent<U>& Handle, T* GarbageCollectedObject)
+	{		
+		typedef TPair<FJavascriptIsolateImplementation*, T*> WeakData;
+		typedef TPairInitializer<WeakData::KeyInitType, WeakData::ValueInitType> InitializerType;
+
+		Handle.SetWeak<WeakData>(new WeakData(InitializerType(this, GarbageCollectedObject)), [](const WeakCallbackData<U, WeakData>& data) {
+			auto Parameter = data.GetParameter();
+
+			Parameter->Key->OnGarbageCollectedByV8(Parameter->Value);
+
+			delete Parameter;
+		});
+	}
+
+	template <typename StructType>
+	void RegisterStruct(TMap< StructType*, v8::UniquePersistent<v8::FunctionTemplate> >& TheMap, StructType* Class, Local<FunctionTemplate> Template)
+	{
+		FIsolateHelper I(isolate_);
+
+		// public name
+		auto name = I.Keyword(FV8Config::Safeify(Class->GetName()));
+
+		// If we are running in a context, we also register this class to the context directly.
+		auto Context = isolate_->GetCurrentContext();
+		if (!Context.IsEmpty())
+		{
+			Context->Global()->Set(name, Template->GetFunction());
+		}
+
+		// Register this class to the global template so that any other contexts which will be created later have this function template.
+		GetGlobalTemplate()->Set(name, Template);
+
+		// Track this class from v8 gc.
+		auto& result = TheMap.Add(Class, UniquePersistent<FunctionTemplate>(isolate_, Template));
+		SetWeak(result, Class);
+	}
+
+	virtual void RegisterClass(UClass* Class, Local<FunctionTemplate> Template) override
+	{
+		RegisterStruct(ClassToFunctionTemplateMap, Class, Template);		
+	}
+
+	void RegisterScriptStruct(UScriptStruct* Struct, Local<FunctionTemplate> Template)
+	{
+		RegisterStruct(ScriptStructToFunctionTemplateMap, Struct, Template);		
+	}
+
+	void RegisterObject(UObject* UnrealObject, Local<Value> value)
+	{		
+		auto& result = GetContext()->ObjectToObjectMap.Add(UnrealObject, UniquePersistent<Value>(isolate_, value));
+		SetWeak(result, UnrealObject);		
+	}				
+
+	void RegisterScriptStructInstance(TSharedPtr<FStructMemoryInstance> MemoryObject, Local<Value> value)
+	{
+		auto& result = GetContext()->MemoryToObjectMap.Add(MemoryObject, UniquePersistent<Value>(isolate_, value));
+		SetWeak(result, MemoryObject.Get());
+	}
+
+	void OnGarbageCollectedByV8(FStructMemoryInstance* Memory)
+	{
+		// We should keep ourselves clean
+		GetContext()->MemoryToObjectMap.Remove(Memory->AsShared());
+	}
+
+	void OnGarbageCollectedByV8(UObject* Object)
+	{
+		if (auto klass = Cast<UClass>(Object))
+		{
+			ClassToFunctionTemplateMap.Remove(klass);
+		}
+
+		GetContext()->ObjectToObjectMap.Remove(Object);		
+	}	
+
+	static FJavascriptIsolateImplementation* GetSelf(Isolate* isolate)
+	{
+		return reinterpret_cast<FJavascriptIsolateImplementation*>(isolate->GetData(0));
+	}
+};
+
+FJavascriptIsolate* FJavascriptIsolate::Create()
+{
+	return new FJavascriptIsolateImplementation();
+}
+
+Local<Value> FJavascriptIsolate::ReadProperty(Isolate* isolate, UProperty* Property, uint8* Buffer, const IPropertyOwner& Owner)
+{
+	return FJavascriptIsolateImplementation::GetSelf(isolate)->InternalReadProperty(Property, Buffer, Owner);
+}
+
+void FJavascriptIsolate::WriteProperty(Isolate* isolate, UProperty* Property, uint8* Buffer, Handle<Value> Value)
+{
+	FJavascriptIsolateImplementation::GetSelf(isolate)->InternalWriteProperty(Property, Buffer, Value);
+}
+
+void FPendingClassConstruction::Finalize(FJavascriptIsolate* Isolate, UObject* UnrealObject)
+{
+	static_cast<FJavascriptIsolateImplementation*>(Isolate)->RegisterObject(UnrealObject, Object);
+	Object->SetAlignedPointerInInternalField(0, UnrealObject);
+}
+
+template <typename CppType>
+bool TStructReader<CppType>::Read(Isolate* isolate, Local<Value> Value, CppType& Target) const
+{
+	FIsolateHelper I(isolate);
+
+	auto Instance = FStructMemoryInstance::FromV8(Value);
+	if (Instance && Instance->Struct == ScriptStruct)
+	{
+		ScriptStruct->CopyScriptStruct(&Target, Instance->GetMemory());
+	}
+	else if (Value->IsObject())
+	{
+		auto v8_v1 = Value->ToObject();
+
+		FJavascriptIsolateImplementation::GetSelf(isolate)->ReadOffStruct(v8_v1, ScriptStruct, reinterpret_cast<uint8*>(&Target));
+	}
+	else
+	{
+		I.Throw(TEXT("couldn't read struct"));
+		return false;
+	}
+
+	return true;
+}
+
+namespace v8
+{
+	Local<Value> ReadProperty(Isolate* isolate, UProperty* Property, uint8* Buffer, const IPropertyOwner& Owner)
+	{
+		return FJavascriptIsolate::ReadProperty(isolate, Property, Buffer, Owner);
+	}
+
+	void WriteProperty(Isolate* isolate, UProperty* Property, uint8* Buffer, Local<Value> Value)
+	{
+		FJavascriptIsolate::WriteProperty(isolate, Property, Buffer, Value);
+	}
+}
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptIsolate_Private.h b/Plugins/UnrealJS/Source/V8/Private/JavascriptIsolate_Private.h
new file mode 100644
index 0000000..8b0e736
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptIsolate_Private.h
@@ -0,0 +1,45 @@
+#pragma once
+
+struct FStructMemoryInstance;
+
+struct FPendingClassConstruction
+{
+	FPendingClassConstruction() {}
+	FPendingClassConstruction(Handle<v8::Object> InObject, UClass* InClass)
+		: Object(InObject), Class(InClass)
+	{}
+
+	Handle<v8::Object> Object;
+	UClass* Class;
+	bool bCatched{ false };
+
+	void Finalize(FJavascriptIsolate* Isolate, UObject* Object);
+};
+
+class FJavascriptIsolate
+{
+public:
+	/** A map from Unreal UClass to V8 Function template */
+	TMap< UClass*, v8::UniquePersistent<v8::FunctionTemplate> > ClassToFunctionTemplateMap;
+
+	/** A map from Unreal UScriptStruct to V8 Function template */
+	TMap< UScriptStruct*, v8::UniquePersistent<v8::FunctionTemplate> > ScriptStructToFunctionTemplateMap;	
+
+	/** BlueprintFunctionLibrary function mapping */
+	TMultiMap< const UClass*, UFunction*> BlueprintFunctionLibraryMapping;
+
+	TArray<FPendingClassConstruction> ObjectUnderConstructionStack;
+
+	v8::Isolate* isolate_;
+
+	static FJavascriptIsolate* Create();
+	static v8::Local<v8::Value> ReadProperty(v8::Isolate* isolate, UProperty* Property, uint8* Buffer, const IPropertyOwner& Owner);
+	static void WriteProperty(v8::Isolate* isolate, UProperty* Property, uint8* Buffer, v8::Handle<v8::Value> Value);
+
+	virtual v8::Local<v8::Value> ExportObject(UObject* Object, bool bForce = false) = 0;
+	virtual v8::Local<v8::FunctionTemplate> ExportClass(UClass* Class, bool bAutoRegister = true) = 0;
+	virtual void RegisterClass(UClass* Class, v8::Local<v8::FunctionTemplate> Template) = 0;
+	virtual v8::Local<v8::ObjectTemplate> GetGlobalTemplate() = 0;
+	virtual void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) = 0;
+	virtual ~FJavascriptIsolate() {}	
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptLibrary.cpp b/Plugins/UnrealJS/Source/V8/Private/JavascriptLibrary.cpp
new file mode 100644
index 0000000..9ce6abd
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptLibrary.cpp
@@ -0,0 +1,118 @@
+#include "V8PCH.h"
+#include "JavascriptLibrary.h"
+#include "Engine/DynamicBlueprintBinding.h"
+
+void UJavascriptLibrary::SetMobile(USceneComponent* SceneComponent)
+{
+	if (SceneComponent)
+	{
+		SceneComponent->SetMobility(EComponentMobility::Movable);
+	}
+}
+
+void UJavascriptLibrary::SetMobility(USceneComponent* SceneComponent, EComponentMobility::Type Type)
+{
+	if (SceneComponent)
+	{
+		SceneComponent->SetMobility(Type);
+	}
+}
+
+UWorld* UJavascriptLibrary::Actor_GetWorld(AActor* Actor)
+{
+	return Actor ? Actor->GetWorld() : nullptr;
+}
+
+UClass* UJavascriptLibrary::GetBlueprintGeneratedClass(UBlueprint* Blueprint)
+{
+	UE_LOG(Javascript, Warning, TEXT("GetBlueprintGeneratedClass will be deprecated : Use instead Blueprint.GeneratedClass"));
+	return Blueprint ? Blueprint->GeneratedClass : nullptr;
+}
+
+UClass* UJavascriptLibrary::GetBlueprintGeneratedClassFromPath(FString Path)
+{
+	UE_LOG(Javascript, Warning, TEXT("GetBlueprintGeneratedClassFromPath will be deprecated : Use instead Blueprint.Load(Path).GeneratedClass"));
+	return GetBlueprintGeneratedClass(Cast<UBlueprint>(StaticLoadObject(UBlueprint::StaticClass(), nullptr, *Path)));
+}
+
+UObject* UJavascriptLibrary::GetOuter(UObject* Object)
+{
+	return Object ? Object->GetOuter() : nullptr;
+}
+
+UObject* UJavascriptLibrary::GetOutermost(UObject* Object)
+{
+	return Object ? Object->GetOutermost() : nullptr;
+}
+
+FString UJavascriptLibrary::GetName(UObject* Object)
+{
+	return Object ? Object->GetName() : TEXT("");
+}
+
+bool UJavascriptLibrary::IsPlayInEditor(UWorld* World)
+{
+	return World && World->IsPlayInEditor();
+}
+
+bool UJavascriptLibrary::IsPlayInPreview(UWorld* World)
+{
+	return World && World->IsPlayInPreview();
+}
+
+bool UJavascriptLibrary::IsGameWorld(UWorld* World)
+{
+	return World && World->IsGameWorld();
+}
+
+void UJavascriptLibrary::SetClientTravel(UEngine* Engine, UWorld *InWorld, FString NextURL, ETravelType InTravelType)
+{
+	if (Engine)
+	{
+		Engine->SetClientTravel(InWorld, *NextURL, InTravelType);
+	}
+}
+
+UPackage* UJavascriptLibrary::CreatePackage(UObject* Outer, FString PackageName)
+{
+	return ::CreatePackage(Outer, *PackageName);
+}
+
+UActorComponent* UJavascriptLibrary::FindComponentObjectByName(AActor* Actor, const FName& InName)
+{
+	if (Actor != nullptr && ::IsValid(Actor) == true)
+	{
+		return Actor->FindComponentByName<UActorComponent>(InName);
+	}
+
+	return nullptr;
+}
+
+void UJavascriptLibrary::AddDynamicBinding(UClass* Outer, UDynamicBlueprintBinding* BindingObject)
+{
+	if (Cast<UBlueprintGeneratedClass>(Outer) && BindingObject)
+	{
+		Cast<UBlueprintGeneratedClass>(Outer)->DynamicBindingObjects.Add(BindingObject);
+	}	
+}
+
+UDynamicBlueprintBinding* UJavascriptLibrary::GetDynamicBinding(UClass* Outer, TSubclassOf<UDynamicBlueprintBinding> BindingObjectClass)
+{
+	if (auto bpg = Cast<UBlueprintGeneratedClass>(Outer))
+	{
+		for (auto BindingObject : bpg->DynamicBindingObjects)
+		{
+			if (BindingObject->IsA(BindingObjectClass))
+			{
+				return BindingObject;
+			}
+		}
+	}
+
+	return nullptr;
+}
+
+void UJavascriptLibrary::HandleSeamlessTravelPlayer(AGameMode* GameMode, AController*& C)
+{
+	GameMode->HandleSeamlessTravelPlayer(C);
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/JavascriptProcess.cpp b/Plugins/UnrealJS/Source/V8/Private/JavascriptProcess.cpp
new file mode 100644
index 0000000..5ac4e5f
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/JavascriptProcess.cpp
@@ -0,0 +1,78 @@
+#include "V8PCH.h"
+#include "JavascriptProcess.h"
+
+UJavascriptProcess::UJavascriptProcess(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{	
+}
+
+UJavascriptProcess* UJavascriptProcess::Create(const FString& URL, const FString& Parms, bool bLaunchDetached, bool bLaunchHidden, bool bLaunchReallyHidden, int32 PriorityModifier, const FString& OptionalWorkingDirectory, bool bUsePipe)
+{
+	uint32 ProcessID;
+	void* ReadPipe{ nullptr };
+	void* WritePipe{ nullptr };
+	if (bUsePipe)
+	{
+		FPlatformProcess::CreatePipe(ReadPipe, WritePipe);
+	}
+	auto Handle = FPlatformProcess::CreateProc(*URL, *Parms, bLaunchDetached, bLaunchHidden, bLaunchReallyHidden, &ProcessID, PriorityModifier, OptionalWorkingDirectory.Len() ? *OptionalWorkingDirectory : nullptr, WritePipe);
+	if (Handle.IsValid())
+	{
+		auto Instance = NewObject<UJavascriptProcess>();
+		Instance->ProcessHandle = Handle;
+		Instance->ProcessID = ProcessID;
+		Instance->ReadPipe = ReadPipe;
+		Instance->WritePipe = WritePipe;
+		return Instance;
+	}
+	else
+	{
+		return nullptr;
+	}
+}
+
+bool UJavascriptProcess::IsRunning()
+{
+	return FPlatformProcess::IsProcRunning(ProcessHandle);
+}
+
+void UJavascriptProcess::Close()
+{
+	FPlatformProcess::CloseProc(ProcessHandle);
+
+	if (ReadPipe || WritePipe)
+	{
+		FPlatformProcess::ClosePipe(ReadPipe, WritePipe);
+		ReadPipe = WritePipe = nullptr;
+	}
+}
+
+FString UJavascriptProcess::ReadFromPipe()
+{
+	return FPlatformProcess::ReadPipe(ReadPipe);
+}
+
+bool UJavascriptProcess::ReadArrayFromPipe(TArray<uint8>& Array)
+{
+	return FPlatformProcess::ReadPipeToArray(ReadPipe, Array);
+}
+
+bool UJavascriptProcess::WriteToPipe(const FString& Message, FString& OutWritten)
+{
+	return FPlatformProcess::WritePipe(WritePipe, Message, &OutWritten);
+}
+
+void UJavascriptProcess::Wait()
+{
+	return FPlatformProcess::WaitForProc(ProcessHandle);
+}
+
+void UJavascriptProcess::Terminate(bool KillTree)
+{
+	return FPlatformProcess::TerminateProc(ProcessHandle, KillTree);
+}
+
+bool UJavascriptProcess::GetReturnCode(int32& ReturnCode)
+{
+	return FPlatformProcess::GetProcReturnCode(ProcessHandle,&ReturnCode);
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/MallocArrayBufferAllocator.h b/Plugins/UnrealJS/Source/V8/Private/MallocArrayBufferAllocator.h
new file mode 100644
index 0000000..40f8023
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/MallocArrayBufferAllocator.h
@@ -0,0 +1,33 @@
+#pragma once
+
+class FMallocArrayBufferAllocator : public ArrayBuffer::Allocator
+{
+public:
+	/**
+	* Allocate |length| bytes. Return NULL if allocation is not successful.
+	* Memory should be initialized to zeroes.
+	*/
+	virtual void* Allocate(size_t length)
+	{
+		auto buffer = GMalloc->Malloc(length);
+		FMemory::Memzero(buffer, length);
+		return buffer;
+	}
+
+	/**
+	* Allocate |length| bytes. Return NULL if allocation is not successful.
+	* Memory does not have to be initialized.
+	*/
+	virtual void* AllocateUninitialized(size_t length)
+	{
+		return GMalloc->Malloc(length);
+	}
+	/**
+	* Free the memory block of size |length|, pointed to by |data|.
+	* That memory is guaranteed to be previously allocated by |Allocate|.
+	*/
+	virtual void Free(void* data, size_t length)
+	{
+		GMalloc->Free(data);
+	}
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/ScopedArguments.h b/Plugins/UnrealJS/Source/V8/Private/ScopedArguments.h
new file mode 100644
index 0000000..17cfa38
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/ScopedArguments.h
@@ -0,0 +1,26 @@
+#pragma once
+
+struct FScopedArguments
+{
+	FScopedArguments(UFunction* InFunction, uint8* InBuffer)
+	: Function(InFunction), Buffer(InBuffer)
+	{
+		for (TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags & CPF_Parm) == CPF_Parm; ++It)
+		{
+			auto Prop = *It;
+			Prop->InitializeValue_InContainer(Buffer);
+		}
+	}
+
+	~FScopedArguments()
+	{
+		for (TFieldIterator<UProperty> It(Function); It && (It->PropertyFlags & CPF_Parm) == CPF_Parm; ++It)
+		{
+			auto Prop = *It;
+			Prop->DestroyValue_InContainer(Buffer);
+		}
+	}
+
+	UFunction* Function;
+	uint8* Buffer;
+};
diff --git a/Plugins/UnrealJS/Source/V8/Private/StructMemoryInstance.h b/Plugins/UnrealJS/Source/V8/Private/StructMemoryInstance.h
new file mode 100644
index 0000000..ad9e33f
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/StructMemoryInstance.h
@@ -0,0 +1,143 @@
+#pragma once
+
+struct FObjectPropertyOwner : IPropertyOwner
+{
+	UObject* Object;
+
+	FObjectPropertyOwner(UObject* InObject)
+		: Object(InObject)
+	{
+		Owner = EPropertyOwner::Object;
+	}
+};
+
+struct FStructMemoryInstance;
+struct FStructMemoryPropertyOwner : IPropertyOwner
+{
+	FStructMemoryInstance* Memory;
+
+	FStructMemoryPropertyOwner(FStructMemoryInstance* InMemory)
+		: Memory(InMemory)
+	{
+		Owner = EPropertyOwner::Memory;
+	}
+};
+
+struct FStructMemoryInstance
+	: public TSharedFromThis<FStructMemoryInstance>
+{
+	FStructMemoryInstance(UScriptStruct* InStruct, const IPropertyOwner& InOwner, void* InSource)
+	: Struct(InStruct), Source(InSource)
+	{
+		Owner = InOwner.Owner;
+		if (Owner == EPropertyOwner::Object)
+		{
+			Object = ((const FObjectPropertyOwner&)InOwner).Object;
+		}
+		else if (Owner == EPropertyOwner::Memory && Source)
+		{
+			auto OwnerInstance = ((const FStructMemoryPropertyOwner&)InOwner).Memory;
+			if (OwnerInstance->Owner == EPropertyOwner::None)
+			{
+				Parent = OwnerInstance->AsShared();
+			}
+			else
+			{
+				Object = OwnerInstance->Object.Get();
+			}
+		}
+
+		if (Owner == EPropertyOwner::None)
+		{
+			Buffer.AddUninitialized(Struct->GetStructureSize());
+			Struct->InitializeStruct(GetMemory());
+
+			if (Source)
+			{
+				//Struct->CopyScriptStruct(GetMemory(), Source);
+				CopyScriptStruct(GetMemory(), Source);
+				Source = nullptr;
+			}
+		}
+	}
+
+	~FStructMemoryInstance()
+	{
+		if (Owner == EPropertyOwner::None)
+		{
+			Struct->DestroyStruct(GetMemory());
+		}
+	}
+
+	// Struct 
+	UScriptStruct* Struct;
+
+	// Type
+	EPropertyOwner Owner;
+
+	// Object which this instance depends on
+	FWeakObjectPtr Object;
+
+	// Memory which this instance refers
+	void* Source;
+
+	// Parent instance
+	TSharedPtr<FStructMemoryInstance> Parent;
+
+	// Independent memory buffer
+	TArray<uint8> Buffer;
+
+	uint8* GetMemory()
+	{
+		if (Owner == EPropertyOwner::None)
+		{
+			return Buffer.GetData();
+		}
+		else if (Object.IsValid())
+		{
+			return (uint8*)Source;
+		}
+		else if (Parent.IsValid())
+		{
+			return (uint8*)Source;
+		}
+		else
+		{
+			return nullptr;
+		}
+	}
+
+	static TSharedRef<FStructMemoryInstance> Create(UScriptStruct* Struct, const IPropertyOwner& InOwner, void* Source = nullptr)
+	{
+		return TSharedRef<FStructMemoryInstance>(new FStructMemoryInstance(Struct, InOwner, Source));
+	}
+
+	static FStructMemoryInstance* FromV8(Local<Value> Value)
+	{
+		auto Memory = RawMemoryFromV8(Value);
+		return reinterpret_cast<FStructMemoryInstance*>(Memory);
+	}
+
+	void CopyScriptStruct(void* InDest, void const* InSrc, int32 ArrayDim = 1)
+	{
+		uint8 *Dest = (uint8*)InDest;
+		check(Dest);
+		uint8 const* Src = (uint8 const*)InSrc;
+		check(Src);
+
+		int32 Stride = Struct->GetStructureSize();
+
+		FMemory::Memcpy(Dest, Src, ArrayDim * Stride);
+
+		if (!(Struct->StructFlags & STRUCT_IsPlainOldData))
+		{
+			for (TFieldIterator<UProperty> It(Struct); It; ++It)
+			{
+				for (int32 Index = 0; Index < ArrayDim; Index++)
+				{
+					It->CopyCompleteValue_InContainer((uint8*)Dest + Index * Stride, (uint8*)Src + Index * Stride);
+				}
+			}
+		}
+	}
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Translator.cpp b/Plugins/UnrealJS/Source/V8/Private/Translator.cpp
new file mode 100644
index 0000000..576b941
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Translator.cpp
@@ -0,0 +1,107 @@
+#include "V8PCH.h"
+#include "Translator.h"
+
+namespace v8
+{
+	UObject* UObjectFromV8(Local<Value> Value)
+	{
+		uint8* Memory = RawMemoryFromV8(Value);
+		if (Memory)
+		{
+			auto uobj = reinterpret_cast<UObject*>(Memory);
+			if (uobj->IsValidLowLevelFast() && !uobj->HasAnyFlags(RF_PendingKill))
+			{
+				return uobj;
+			}
+		}	
+
+		return nullptr;
+	}
+
+	uint8* RawMemoryFromV8(Local<Value> Value)
+	{
+		if (Value.IsEmpty() || !Value->IsObject() || Value->IsUndefined() || Value->IsNull())
+		{
+			return nullptr;
+		}
+
+		auto v8_obj = Value->ToObject();
+		if (v8_obj->InternalFieldCount() == 0)
+		{
+			return nullptr;
+		}
+		return reinterpret_cast<uint8*>(v8_obj->GetAlignedPointerFromInternalField(0));		
+	}
+
+	UClass* UClassFromV8(Isolate* isolate_, Local<Value> Value)
+	{
+		if (Value.IsEmpty())
+		{
+			return nullptr;
+		}
+
+		auto v8_obj = Value->ToObject();
+		if (v8_obj->IsFunction())
+		{
+			auto vv = v8_obj->Get(V8_KeywordString(isolate_, "StaticClass"));
+			if (!vv.IsEmpty())
+			{
+				v8_obj = vv->ToObject();
+			}
+		}
+
+		if (!v8_obj.IsEmpty() && v8_obj->IsExternal())
+		{
+			auto v8_class = Local<External>::Cast(v8_obj);
+
+			UClass* Class{ nullptr };
+			if (!v8_class.IsEmpty())
+			{
+				Class = reinterpret_cast<UClass*>(v8_class->Value());
+			}
+
+			return Class;
+		}
+
+		return nullptr;
+	}
+
+	Local<String> V8_String(Isolate* isolate, const FString& String)
+	{
+		return String::NewFromUtf8(isolate, TCHAR_TO_UTF8(*String));
+	}
+
+	Local<String> V8_String(Isolate* isolate, const char* String)
+	{
+		return String::NewFromUtf8(isolate, String);
+	}
+
+	Local<String> V8_KeywordString(Isolate* isolate, const FString& String)
+	{
+		return String::NewFromUtf8(isolate, TCHAR_TO_UTF8(*String), String::kInternalizedString);
+	}
+
+	Local<String> V8_KeywordString(Isolate* isolate, const char* String)
+	{
+		return String::NewFromUtf8(isolate, String, String::kInternalizedString);
+	}
+
+	FString StringFromV8(Local<Value> Value)
+	{
+		return UTF8_TO_TCHAR(*String::Utf8Value(Value));
+	}
+
+	FString StringFromArgs(const FunctionCallbackInfo<v8::Value>& args, int StartIndex)
+	{
+		HandleScope handle_scope(args.GetIsolate());
+
+		TArray<FString> ArgStrings;
+
+		for (int Index = StartIndex; Index < args.Length(); Index++)
+		{
+			ArgStrings.Add(StringFromV8(args[Index]));
+		}
+
+		return FString::Join(ArgStrings, TEXT(" "));
+	}
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/Translator.h b/Plugins/UnrealJS/Source/V8/Private/Translator.h
new file mode 100644
index 0000000..bc48053
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/Translator.h
@@ -0,0 +1,38 @@
+#pragma once
+
+enum class EPropertyOwner
+{
+	None,
+	Object,
+	Memory
+};
+
+struct IPropertyOwner
+{
+	EPropertyOwner Owner;
+};
+
+struct FNoPropertyOwner : IPropertyOwner
+{
+	FNoPropertyOwner()
+	{
+		Owner = EPropertyOwner::None;
+	}
+};
+
+namespace v8
+{
+	Local<Value> ReadProperty(Isolate* isolate, UProperty* Property, uint8* Buffer, const IPropertyOwner& Owner);
+	void WriteProperty(Isolate* isolate, UProperty* Property, uint8* Buffer, Local<Value> value);
+	void ReportException(Isolate* isolate, TryCatch& try_catch);
+	Local<String> V8_String(Isolate* isolate, const FString& String);
+	Local<String> V8_String(Isolate* isolate, const char* String);
+	Local<String> V8_KeywordString(Isolate* isolate, const FString& String);
+	Local<String> V8_KeywordString(Isolate* isolate, const char* String);
+	FString StringFromV8(Local<Value> Value);
+	void CallJavascriptFunction(Handle<Context> context, Handle<Value> This, UFunction* SignatureFunction, Handle<Function> func, void* Parms);
+	UClass* UClassFromV8(Isolate* isolate_, Local<Value> Value);
+	UObject* UObjectFromV8(Local<Value> Value);
+	uint8* RawMemoryFromV8(Local<Value> Value);
+	FString StringFromArgs(const FunctionCallbackInfo<v8::Value>& args, int StartIndex = 0);
+}
diff --git a/Plugins/UnrealJS/Source/V8/Private/TypingGenerator.h b/Plugins/UnrealJS/Source/V8/Private/TypingGenerator.h
new file mode 100644
index 0000000..a1a20ef
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/TypingGenerator.h
@@ -0,0 +1,525 @@
+#pragma once
+
+#include "Config.h"
+
+struct TypingGeneratorBase
+{
+	TSet<UObject*> visited;
+
+	void mark_visited(UObject* obj)
+	{
+		visited.Add(obj);
+	}
+
+	bool has_visited(UObject* obj) const
+	{
+		return (visited.Find(obj) != nullptr);
+	}
+
+	void Export(UObject* source)
+	{
+		if (has_visited(source)) return;
+		mark_visited(source);
+
+		//UE_LOG(Javascript, Log, TEXT("Export %s"), *(source->GetName()));
+
+		if (auto s = Cast<UClass>(source))
+		{
+			ExportClass(s);
+		}
+		else if (auto s = Cast<UStruct>(source))
+		{
+			ExportStruct(s);
+		}
+		else if (auto s = Cast<UEnum>(source))
+		{
+			ExportEnum(s);
+		}
+	}
+
+	virtual void ExportClass(UClass* source) {}
+	virtual void ExportStruct(UStruct* source) {}
+	virtual void ExportEnum(UEnum* source) {}
+};
+
+struct TokenWriter
+{
+	TokenWriter(TypingGeneratorBase& generator)
+	: generator(generator)
+	{}
+
+	TypingGeneratorBase& generator;
+
+	FString Text;
+
+	void push(const char* something)
+	{
+		Text.Append(ANSI_TO_TCHAR(something));
+	}
+
+	void push(const FString& something)
+	{
+		Text.Append(something);
+	}
+
+	const TCHAR* operator * ()
+	{
+		return *Text;
+	}
+	
+	void push(UProperty* Property)
+	{
+		if (auto p = Cast<UIntProperty>(Property))
+		{
+			push("number");
+		}
+		else if (auto p = Cast<UFloatProperty>(Property))
+		{
+			push("number");
+		}
+		else if (auto p = Cast<UBoolProperty>(Property))
+		{
+			push("boolean");
+		}
+		else if (auto p = Cast<UNameProperty>(Property))
+		{
+			push("string");
+		}
+		else if (auto p = Cast<UStrProperty>(Property))
+		{
+			push("string");
+		}
+		else if (auto p = Cast<UTextProperty>(Property))
+		{
+			push("string");
+		}
+		else if (auto p = Cast<UClassProperty>(Property))
+		{
+			generator.Export(p->MetaClass);
+
+			// @HACK
+			push("UnrealEngineClass");
+		}
+		else if (auto p = Cast<UStructProperty>(Property))
+		{
+			generator.Export(p->Struct);
+			push(FV8Config::Safeify(p->Struct->GetName()));
+		}
+		else if (auto p = Cast<UArrayProperty>(Property))
+		{
+			generator.Export(p->Inner);
+
+			push(p->Inner);
+			push("[]");
+		}
+		else if (auto p = Cast<UByteProperty>(Property))
+		{
+			if (p->Enum)
+			{
+				generator.Export(p->Enum);
+				push(FV8Config::Safeify(p->Enum->GetName()));
+			}
+			else
+			{
+				push("number");
+			}
+		}
+		else if (auto p = Cast<UMulticastDelegateProperty>(Property))
+		{
+			push("UnrealEngineMulticastDelegate<");
+			push(p->SignatureFunction);
+			push(">");
+		}
+		else if (auto p = Cast<UDelegateProperty>(Property))
+		{
+			push("UnrealEngineDelegate<");
+			push(p->SignatureFunction);
+			push(">");
+		}
+		else if (auto p = Cast<UObjectProperty>(Property))
+		{
+			generator.Export(p->PropertyClass);
+			push(FV8Config::Safeify(p->PropertyClass->GetName()));
+		}
+		else
+		{
+			push("any");
+		}
+	}
+
+	void push(UFunction* SignatureFunction)
+	{
+		push("(");
+		bool first = true;
+		TFieldIterator<UProperty> It(SignatureFunction);
+		for (; It && (It->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++It)
+		{
+			auto Prop = *It;
+			if (!first) push(", ");
+			push(FV8Config::Safeify(Prop->GetName()));
+			push(": ");
+			push(Prop);
+			first = false;
+		}
+		push(") => ");
+		bool has_return_value = false;
+		for (; It; ++It)
+		{
+			UProperty* Param = *It;
+			if (Param->GetPropertyFlags() & CPF_ReturnParm)
+			{
+				has_return_value = true;
+				push(Param);
+				break;
+			}
+		}
+		if (!has_return_value) push("void");
+	}
+
+	void tooltip(const char* indent, UField* source)
+	{
+		FString Tooltip = source->GetToolTipText().ToString();
+		if (Tooltip.Len() > 0)
+		{
+			TArray<FString> Lines;
+			Tooltip.ParseIntoArrayLines(Lines);
+
+			bool first_line = true;
+			push(indent);
+			push("/**\n");
+			for (const auto& line : Lines)
+			{
+				push(indent);
+				push(" * ");
+				push(line);
+				push("\n");
+			}
+			push(indent);
+			push("*/\n");
+		}
+	}	
+};
+
+struct TypingGenerator : TypingGeneratorBase
+{
+	TypingGenerator(FJavascriptIsolate& InEnvironment)
+	: Environment(InEnvironment)
+	{}
+
+	FJavascriptIsolate& Environment;
+
+	FString Text;
+
+	TArray<FString> Folded;
+
+	void fold(bool force = false)
+	{
+		if (force || Text.Len() > 1024 * 1024)
+		{
+			Folded.Add(Text);
+			Text = TEXT("");
+		}
+	}
+
+	virtual void ExportEnum(UEnum* source) override
+	{
+		TokenWriter w(*this);
+
+		w.push("declare type ");
+		w.push(FV8Config::Safeify(source->GetName()));
+		w.push(" = string;\n");
+
+		Text.Append(*w);
+
+		fold();
+	}
+
+	virtual void ExportClass(UClass* source) override
+	{
+		// Skip a generated class
+		if (source->ClassGeneratedBy) return;
+
+		ExportStruct(source);
+	}
+
+	virtual void ExportStruct(UStruct* source) override
+	{
+		TokenWriter w(*this);		
+
+		const auto name = FV8Config::Safeify(source->GetName());
+		auto super_class = source->GetSuperStruct();
+
+		w.push("declare class ");
+		w.push(name);
+
+		if (super_class)
+		{
+			Export(super_class);
+
+			w.push(" extends ");
+			w.push(FV8Config::Safeify(super_class->GetName()));
+		}
+		w.push(" { \n");
+
+		for (TFieldIterator<UProperty> PropertyIt(source, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt)
+		{
+			auto Property = *PropertyIt;
+			auto PropertyName = FV8Config::Safeify(Property->GetName());
+
+			w.tooltip("\t", Property);
+
+			w.push("\t");
+			w.push(PropertyName);
+			w.push(": ");
+			w.push(Property);
+			w.push(";\n");
+		}
+
+		if (auto klass = Cast<UClass>(source))
+		{
+			if (klass->IsChildOf(AActor::StaticClass()))
+			{
+				Export(UWorld::StaticClass());
+				w.push("\tconstructor(InWorld: World, Location?: Vector, Rotation?: Rotator);\n");
+			}
+			else
+			{
+				w.push("\tconstructor();\n");
+				w.push("\tconstructor(Outer: UObject);\n");
+				w.push("\tstatic Load(ResourceName: string): ");
+				w.push(name);
+				w.push(";\n");
+			}
+
+			w.push("\tstatic StaticClass: any;\n");
+
+			w.push("\tstatic GetClassObject(): UClass;\n");
+
+			w.push("\tstatic GetDefaultObject(): ");
+			w.push(name);
+			w.push(";\n");
+
+			w.push("\tstatic GetDefaultSubobjectByName(Name: string): UObject;\n");
+
+			w.push("\tstatic CreateDefaultSubobject(Name: string, Transient?: boolean, Required?: boolean, Abstract?: boolean): ");
+			w.push(name);
+			w.push(";\n");
+
+			w.push("\tstatic C(Other: UObject): ");
+			w.push(name);
+			w.push(";\n");
+
+			auto write_function = [&](UFunction* Function, bool is_thunk) {
+				w.tooltip("\t", Function);
+
+				w.push("\t");
+				if (!is_thunk && (Function->FunctionFlags & FUNC_Static))
+				{
+					w.push("static ");
+				}
+				w.push(FV8Config::GetAlias(Function, true));
+				w.push("(");
+
+				bool has_out_ref = false;
+				bool is_optional = false;
+
+				TArray<FString> Arguments;
+				for (TFieldIterator<UProperty> ParamIt(Function); ParamIt && (ParamIt->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm; ++ParamIt)
+				{
+					TokenWriter w2(*this);
+
+					if ((ParamIt->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+					{
+						has_out_ref = true;
+						is_optional = true;
+					}
+
+					auto Property = *ParamIt;
+					auto PropertyName = FV8Config::Safeify(Property->GetName());
+
+					w2.push(PropertyName);
+					if (is_optional)
+					{
+						w2.push("?");
+					}
+					w2.push(": ");
+					w2.push(Property);
+
+					Arguments.Add(*w2);
+				}
+
+				if (is_thunk)
+				{
+					Arguments.RemoveAt(0);
+				}
+
+				w.push(FString::Join(Arguments, TEXT(",")));
+				w.push("): ");
+
+				if (has_out_ref)
+				{
+					TArray<FString> Arguments;
+					for (TFieldIterator<UProperty> ParamIt(Function); ParamIt; ++ParamIt)
+					{
+						TokenWriter w2(*this);
+
+						if ((ParamIt->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == (CPF_Parm | CPF_ReturnParm))
+						{
+							w2.push("$: ");
+							w2.push(*ParamIt);
+
+							Arguments.Add(*w2);
+						}
+						else if ((ParamIt->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm)
+						{
+							w2.push(ParamIt->GetName());
+							w2.push(": ");
+							w2.push(*ParamIt);
+
+							Arguments.Add(*w2);
+						}
+					}
+					w.push("{");
+					w.push(FString::Join(Arguments, TEXT(", ")));
+					w.push("}");
+				}
+				else
+				{
+					bool has_return = false;
+					for (TFieldIterator<UProperty> ParamIt(Function); ParamIt; ++ParamIt)
+					{
+						if ((ParamIt->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == (CPF_Parm | CPF_ReturnParm))
+						{
+							w.push(*ParamIt);
+							has_return = true;
+							break;
+						}
+					}
+
+					if (!has_return)
+					{
+						w.push("void");
+					}
+				}
+
+
+				w.push(";\n");
+			};
+
+			TArray<UFunction*> Functions;
+			Environment.BlueprintFunctionLibraryMapping.MultiFind(klass, Functions);
+
+			for (auto Function : Functions)
+			{
+				write_function(Function, true);
+			}
+
+			for (TFieldIterator<UFunction> FuncIt(klass, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
+			{
+				UFunction* Function = *FuncIt;
+
+				if (!FV8Config::CanExportFunction(klass, Function)) continue;
+
+				write_function(Function,false);
+			}
+		}
+
+		w.push("}\n\n");
+		
+		Text.Append(*w);
+
+		fold();
+	}
+
+	void ExportBootstrap()
+	{
+		TokenWriter w(*this);
+		w.push("declare function gc() : void;\n");
+		w.push("declare type UnrealEngineClass = any;\n");
+
+		w.push("declare type timeout_handle = any;\n");
+		w.push("declare function setTimeout(fn : (milliseconds: number) => void, timeout : number) : timeout_handle;\n");
+		w.push("declare function clearTimeout(handle : timeout_handle) : void;\n");
+
+		w.push("declare class UnrealEngineMulticastDelegate<T> {\n");
+		w.push("\tAdd(fn : T): void;\n");
+		w.push("\tRemove(fn : T): void;\n");
+		w.push("}\n\n");
+
+		w.push("declare class UnrealEngineDelegate<T> {\n");
+		w.push("\tAdd(fn : T): void;\n");
+		w.push("\tRemove(fn : T): void;\n");
+		w.push("}\n\n");
+
+		w.push("declare class Process {\n");
+		w.push("\tnextTick(fn : (number) => void): void;\n");		
+		w.push("}\n\n");
+		w.push("declare var process : Process;\n\n");
+
+		w.push("declare class Memory {\n");
+		w.push("\tbind(ab : ArrayBuffer): void;\n");
+		w.push("\tunbind(): void;\n");
+		w.push("}\n\n");
+		w.push("declare var memory : Memory;\n\n");
+
+		Text.Append(*w);
+	}
+
+	void ExportWKO(FString name, UObject* Object)
+	{
+		TokenWriter w(*this);
+
+		Export(Object->GetClass());
+
+		w.push("declare var ");
+		w.push(name);
+		w.push(" : ");
+		w.push(FV8Config::Safeify(Object->GetClass()->GetName()));
+
+		w.push(";\n\n");
+
+		Text.Append(*w);
+	}
+
+	void Finalize()
+	{
+		fold(true);
+	}
+
+	bool Save(const FString& Filename)
+	{
+		FString Path, BaseFilename, Extension;
+
+		FPaths::Split(Filename, Path, BaseFilename, Extension);
+
+		for (int32 Index = 0; Index < Folded.Num(); ++Index)
+		{
+			const bool is_last = (Index == (Folded.Num() - 1));
+
+			FString Text = Folded[Index];
+
+			auto page_name = [&](int32 Index) {
+				return FString::Printf(TEXT("_part_%d_%s.%s"), Index, *BaseFilename, *Extension);
+			};
+
+			FString PageFilename = is_last ? Filename : FPaths::Combine(*Path, *page_name(Index));
+
+			if (is_last)
+			{
+				FString Header;
+
+				for (int32 Prev = 0; Prev < Index; ++Prev)
+				{
+					Header.Append(FString::Printf(TEXT("/// <reference path=\"%s\">/>\n"), *page_name(Prev)));
+				}
+
+				Header.Append(Text);
+				Text = Header;
+			}
+
+			if (!FFileHelper::SaveStringToFile(Text, *PageFilename)) return false;
+		}
+
+		return true;
+	}
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/V8Implementation.cpp b/Plugins/UnrealJS/Source/V8/Private/V8Implementation.cpp
new file mode 100644
index 0000000..7f79c84
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/V8Implementation.cpp
@@ -0,0 +1,294 @@
+#include "V8PCH.h"
+#include "JavascriptIsolate.h"
+#include "JavascriptContext.h"
+#include "JavascriptComponent.h"
+#include "Config.h"
+#include "Translator.h"
+#include "Exception.h"
+
+#include "JavascriptIsolate_Private.h"
+#include "JavascriptContext_Private.h"
+
+using namespace v8;
+
+DEFINE_LOG_CATEGORY(Javascript);
+
+UJavascriptIsolate::UJavascriptIsolate(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+	const bool bIsClassDefaultObject = IsTemplate(RF_ClassDefaultObject);
+	if (!bIsClassDefaultObject)
+	{
+		JavascriptIsolate = TSharedPtr<FJavascriptIsolate>(FJavascriptIsolate::Create());
+	}
+}
+
+void UJavascriptIsolate::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
+{
+	UJavascriptIsolate* This = CastChecked<UJavascriptIsolate>(InThis);
+
+	if (This->JavascriptIsolate.IsValid())
+	{
+		Collector.AllowEliminatingReferences(false);
+		
+		This->JavascriptIsolate->AddReferencedObjects(This, Collector);		
+
+		Collector.AllowEliminatingReferences(true);
+	}		
+	
+	Super::AddReferencedObjects(This, Collector);
+}
+
+UJavascriptIsolate::~UJavascriptIsolate()
+{
+	const bool bIsClassDefaultObject = IsTemplate(RF_ClassDefaultObject);
+	if (!bIsClassDefaultObject)
+	{
+		JavascriptIsolate.Reset();
+	}
+}
+
+UJavascriptContext* UJavascriptIsolate::CreateContext()
+{
+	return NewObject<UJavascriptContext>(this);
+}
+
+UJavascriptContext::UJavascriptContext(const FObjectInitializer& ObjectInitializer)
+: Super(ObjectInitializer)
+{
+	const bool bIsClassDefaultObject = IsTemplate(RF_ClassDefaultObject);
+	if (!bIsClassDefaultObject)
+	{
+		auto Isolate = Cast<UJavascriptIsolate>(GetOuter());
+		JavascriptContext = TSharedPtr<FJavascriptContext>(FJavascriptContext::Create(Isolate->JavascriptIsolate,Paths));
+
+		Expose("Context", this);
+
+		SetContextId(GetName());
+	}	
+}
+
+void UJavascriptContext::SetContextId(FString Name)
+{
+	ContextId = MakeShareable(new FString(Name));
+}
+
+void UJavascriptContext::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
+{
+	UJavascriptContext* This = CastChecked<UJavascriptContext>(InThis);
+
+	if (This->JavascriptContext.IsValid())
+	{
+		Collector.AllowEliminatingReferences(false);
+
+		This->JavascriptContext->AddReferencedObjects(This, Collector);
+
+		Collector.AllowEliminatingReferences(true);
+	}
+
+	Super::AddReferencedObjects(This, Collector);
+}
+
+void UJavascriptContext::Expose(FString Name, UObject* Object)
+{
+	JavascriptContext->Expose(Name, Object);
+}
+
+FString UJavascriptContext::GetScriptFileFullPath(FString Filename)
+{
+	return JavascriptContext->GetScriptFileFullPath(Filename);
+}
+
+FString UJavascriptContext::ReadScriptFile(FString Filename)
+{
+	return JavascriptContext->ReadScriptFile(Filename);
+}
+
+void UJavascriptContext::RunFile(FString Filename)
+{
+	JavascriptContext->Public_RunFile(Filename);
+}
+
+FString UJavascriptContext::RunScript(FString Script, bool bOutput)
+{
+	return JavascriptContext->Public_RunScript(Script, bOutput);	
+}
+
+void UJavascriptContext::SetAsDebugContext()
+{
+	JavascriptContext->SetAsDebugContext();
+}
+
+bool UJavascriptContext::IsDebugContext() const
+{
+	return JavascriptContext->IsDebugContext();
+}
+
+bool UJavascriptContext::WriteAliases(FString Filename)
+{
+	return JavascriptContext->WriteAliases(Filename);
+}
+
+bool UJavascriptContext::WriteDTS(FString Filename)
+{
+	return JavascriptContext->WriteDTS(Filename);
+}
+
+bool UJavascriptContext::HasProxyFunction(UObject* Holder, UFunction* Function)
+{
+	return JavascriptContext->HasProxyFunction(Holder, Function);
+}
+
+bool UJavascriptContext::CallProxyFunction(UObject* Holder, UObject* This, UFunction* FunctionToCall, void* Parms)
+{
+	return JavascriptContext->CallProxyFunction(Holder, This, FunctionToCall, Parms);
+}
+
+namespace fastcall
+{
+	enum { MaxArgs = 32 };
+
+	Handle<Value> argv[MaxArgs];
+	Handle<Value> retval;
+	int argc = 0;
+
+	void push(Local<Value> value)
+	{
+		argv[argc++] = value;
+	}
+
+	void call(Local<Object> self, Local<Function> func)
+	{
+		retval = func->Call(self, argc, argv);
+	}
+
+	void reset()
+	{
+		for (int32 Index = 0; Index < argc; ++Index)
+		{
+			argv[Index].Clear();
+		}
+		argc = 0;
+		retval.Clear();
+	}
+	
+	uint8 handle_scope_raw[sizeof(HandleScope)];
+
+	void enter(Isolate* isolate)
+	{
+		isolate->Enter();
+		new(reinterpret_cast<HandleScope*>(handle_scope_raw)) HandleScope(isolate);
+	}
+
+	void exit(Isolate* isolate)
+	{
+		reinterpret_cast<HandleScope*>(handle_scope_raw)->~HandleScope();
+		isolate->Exit();		
+	}
+
+	void enter(Local<Context> context)
+	{
+		context->Enter();
+	}
+
+	void exit(Local<Context> context)
+	{
+		context->Exit();		
+	}
+}
+
+void UJavascriptContext::InternalPushArgument(int32 Value)
+{
+	auto isolate = JavascriptContext->isolate();
+	fastcall::push(Int32::New(isolate, Value));
+}
+
+void UJavascriptContext::InternalPushArgument(float Value)
+{
+	auto isolate = JavascriptContext->isolate();
+	fastcall::push(Number::New(isolate, Value));
+}
+
+void UJavascriptContext::InternalPushArgument(bool Value)
+{
+	auto isolate = JavascriptContext->isolate();
+	fastcall::push(Boolean::New(isolate, Value));
+}
+
+void UJavascriptContext::InternalPushArgument(const TCHAR* Value)
+{
+	auto isolate = JavascriptContext->isolate();
+	fastcall::push(V8_String(isolate,Value));
+}
+
+void UJavascriptContext::InternalPushArgument(UObject* Value)
+{
+	fastcall::push(JavascriptContext->ExportObject(Value));
+}
+
+bool UJavascriptContext::InternalPopReturnValue(int32& Value)
+{
+	return !fastcall::retval.IsEmpty() && ((Value = fastcall::retval->Int32Value()), true);
+}
+
+bool UJavascriptContext::InternalPopReturnValue(float& Value)
+{
+	return !fastcall::retval.IsEmpty() && ((Value = fastcall::retval->NumberValue()), true);
+}
+
+bool UJavascriptContext::InternalPopReturnValue(bool& Value)
+{
+	return !fastcall::retval.IsEmpty() && ((Value = fastcall::retval->BooleanValue()), true);
+}
+
+bool UJavascriptContext::InternalPopReturnValue(FString& Value)
+{
+	return !fastcall::retval.IsEmpty() && ((Value = (StringFromV8(fastcall::retval))), true);
+}
+
+bool UJavascriptContext::InternalPopReturnValue(UObject*& Value)
+{
+	return !fastcall::retval.IsEmpty() && ((Value = UObjectFromV8(fastcall::retval)), true);
+}
+
+void UJavascriptContext::InternalBegin()
+{	
+	fastcall::enter(JavascriptContext->isolate());
+	fastcall::enter(JavascriptContext->context());
+}
+
+bool UJavascriptContext::InternalCall(UObject* Object, FName Name)
+{
+	auto context = JavascriptContext->context();
+	
+	auto func = JavascriptContext->GetProxyFunction(Object, *(Name.ToString()));
+	if (!func.IsEmpty() && func->IsFunction())
+	{
+		auto function = Local<Function>::Cast(func);
+
+		TryCatch try_catch;
+		
+		fastcall::call(context->Global(), function);
+
+		if (try_catch.HasCaught())
+		{
+			FV8Exception::Report(try_catch);
+			return false;
+		}
+		else
+		{
+			return true;
+		}		
+	}	
+	else
+	{
+		return false;
+	}	
+}
+
+void UJavascriptContext::InternalEnd()
+{	
+	fastcall::reset();
+	fastcall::exit(JavascriptContext->context());
+	fastcall::exit(JavascriptContext->isolate());
+}
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Private/V8Module.cpp b/Plugins/UnrealJS/Source/V8/Private/V8Module.cpp
new file mode 100644
index 0000000..a66e9ab
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/V8Module.cpp
@@ -0,0 +1,164 @@
+#include "V8PCH.h"
+#include <libplatform/libplatform.h>
+#include "JavascriptContext.h"
+
+using namespace v8;
+
+#include "IV8.h"
+
+class V8Module : public IV8
+{
+
+public:
+	TArray<FString> Paths;
+	v8::Platform* platform_;
+
+	/** IModuleInterface implementation */
+	virtual void StartupModule() override
+	{
+		Paths.Add(GetGameScriptsDirectory());
+		Paths.Add(GetPluginScriptsDirectory());
+		Paths.Add(GetPluginScriptsDirectory2());
+		Paths.Add(GetPluginScriptsDirectory3());
+		Paths.Add(GetPakPluginScriptsDirectory());
+
+		V8::InitializeICU();
+		platform_ = platform::CreateDefaultPlatform();
+		V8::InitializePlatform(platform_);
+		V8::Initialize();
+
+		auto v8flags = "--harmony --harmony-shipping --es-staging --expose-debug-as=v8debug --expose-gc --harmony_destructuring --harmony_simd --harmony_default_parameters ";
+		V8::SetFlagsFromString(v8flags, strlen(v8flags));
+	}
+
+	virtual void ShutdownModule() override
+	{		
+		V8::Dispose();
+		V8::ShutdownPlatform();
+		delete platform_;
+	}
+
+	static FString GetPluginScriptsDirectory()
+	{
+		return FPaths::EnginePluginsDir() / "Backend/UnrealJS/Content/Scripts/";
+	}
+
+	static FString GetPluginScriptsDirectory2()
+	{
+		return FPaths::EnginePluginsDir() / "UnrealJS/Content/Scripts/";
+	}
+
+	static FString GetPluginScriptsDirectory3()
+	{
+		return FPaths::GamePluginsDir() / "UnrealJS/Content/Scripts/";
+	}
+
+	static FString GetPakPluginScriptsDirectory()
+	{
+		return FPaths::EngineDir() / "Contents/Scripts/";
+	}
+
+	static FString GetGameScriptsDirectory()
+	{
+		return FPaths::GameContentDir() / "Scripts/";
+	}
+
+	virtual void AddGlobalScriptSearchPath(const FString& Path) override
+	{
+		Paths.Add(Path);
+	}
+
+	virtual void RemoveGlobalScriptSearchPath(const FString& Path) override
+	{
+		Paths.Remove(Path);
+	}
+
+	virtual TArray<FString> GetGlobalScriptSearchPaths() override
+	{
+		return Paths;
+	}
+
+	virtual void FillAutoCompletion(TSharedPtr<FString> TargetContext, TArray<FString>& OutArray, const TCHAR* Input) override
+	{
+		static auto SourceCode = LR"doc(
+(function () {
+    var pattern = '%s'; var head = '';
+    pattern.replace(/\\W*([\\w\\.]+)$/, function (a, b, c) { head = pattern.substr(0, c + a.length - b.length); pattern = b });
+    var index = pattern.lastIndexOf('.');
+    var scope = this;
+    var left = '';
+    if (index >= 0) {
+        left = pattern.substr(0, index + 1);
+        try { scope = eval(pattern.substr(0, index)); }
+        catch (e) { scope = null; }
+        pattern = pattern.substr(index + 1);
+    }
+    result = [];
+    for (var k in scope) {
+        if (k.indexOf(pattern) == 0) {
+            result.push(head + left + k);
+        }
+    }
+    return result.join(',');
+})()
+)doc";
+
+		for (TObjectIterator<UJavascriptContext> It; It; ++It)
+		{
+			UJavascriptContext* Context = *It;
+
+			if (Context->ContextId == TargetContext || !TargetContext.IsValid() && Context->IsDebugContext())
+			{
+				FString Result = Context->RunScript(FString::Printf(SourceCode, *FString(Input).ReplaceCharWithEscapedChar()), false);
+				Result.ParseIntoArray(OutArray, TEXT(","));
+			}
+		}
+	}
+
+	virtual void Exec(TSharedPtr<FString> TargetContext, const TCHAR* Command) override
+	{
+		for (TObjectIterator<UJavascriptContext> It; It; ++It)
+		{
+			UJavascriptContext* Context = *It;
+
+			if (Context->ContextId == TargetContext || !TargetContext.IsValid() && Context->IsDebugContext())
+			{
+				static FName NAME_JavascriptCmd("JavascriptCmd");
+				GLog->Log(NAME_JavascriptCmd, ELogVerbosity::Log, Command);
+				Context->RunScript(Command);
+			}
+		}
+	}
+
+	virtual bool HasDebugContext() const
+	{
+		for (TObjectIterator<UJavascriptContext> It; It; ++It)
+		{
+			UJavascriptContext* Context = *It;
+
+			if (Context->IsDebugContext())
+			{
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	virtual void GetContextIds(TArray<TSharedPtr<FString>>& OutContexts) override
+	{
+		for (TObjectIterator<UJavascriptContext> It; It; ++It)
+		{
+			UJavascriptContext* Context = *It;
+
+			if (!Context->IsTemplate(RF_ClassDefaultObject))
+			{
+				OutContexts.Add(Context->ContextId);
+			}
+		}
+	}
+};
+
+IMPLEMENT_MODULE(V8Module, V8)
+
+
diff --git a/Plugins/UnrealJS/Source/V8/Private/V8PCH.h b/Plugins/UnrealJS/Source/V8/Private/V8PCH.h
new file mode 100644
index 0000000..2e63be7
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Private/V8PCH.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "CoreUObject.h"
+#include "Engine.h"
+
+#pragma warning( push )
+#pragma warning( disable : 4946 )
+#pragma warning( disable : 4191 )
+
+#include "v8.h"
+#include <v8-debug.h>
+
+#pragma warning( pop )
+
+DECLARE_LOG_CATEGORY_EXTERN(Javascript, Log, All);
+
+struct IJavascriptDebugger
+{
+	virtual ~IJavascriptDebugger() {}
+	
+	virtual void Destroy() = 0;
+
+	static IJavascriptDebugger* Create(int32 InPort, v8::Local<v8::Context> InContext);
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Public/DirectoryWatcher.h b/Plugins/UnrealJS/Source/V8/Public/DirectoryWatcher.h
new file mode 100644
index 0000000..76e27c0
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/DirectoryWatcher.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "IDirectoryWatcher.h"
+#include "DirectoryWatcher.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDirectoryWatcherCallback);
+
+UCLASS()
+class V8_API UDirectoryWatcher : public UObject
+{
+	GENERATED_BODY()
+
+public:	
+	virtual void BeginDestroy() override;
+
+	IDirectoryWatcher::FDirectoryChanged Changed;
+
+	FDelegateHandle DelegateHandle;
+
+	FString Directory;
+
+	UPROPERTY(BlueprintReadOnly, Category = "Javascript")
+	TArray<FString> Added;
+
+	UPROPERTY(BlueprintReadOnly, Category = "Javascript")
+	TArray<FString> Modified;
+
+	UPROPERTY(BlueprintReadOnly, Category = "Javascript")
+	TArray<FString> Removed;
+
+	UPROPERTY(BlueprintAssignable)
+	FDirectoryWatcherCallback OnChanged;
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	bool Contains(const FString& File);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void Watch(const FString& Directory);	
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void Unwatch();
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/IV8.h b/Plugins/UnrealJS/Source/V8/Public/IV8.h
new file mode 100644
index 0000000..9ddb541
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/IV8.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "ModuleManager.h"
+
+/**
+* The public interface to this module. 
+*/
+class IV8 : public IModuleInterface
+{
+public:
+
+	static inline IV8& Get()
+	{
+		return FModuleManager::LoadModuleChecked<IV8>("V8");
+	}
+
+	static inline bool IsAvailable()
+	{
+		return FModuleManager::Get().IsModuleLoaded("V8");
+	}	
+
+	virtual void AddGlobalScriptSearchPath(const FString& Path) = 0;
+	virtual void RemoveGlobalScriptSearchPath(const FString& Path) = 0;
+	virtual TArray<FString> GetGlobalScriptSearchPaths() = 0;
+	virtual bool HasDebugContext() const = 0;
+	virtual void GetContextIds(TArray<TSharedPtr<FString>>& OutContexts) = 0;
+	virtual void FillAutoCompletion(TSharedPtr<FString> TargetContext, TArray<FString>& OutArray, const TCHAR* Input) = 0;
+	virtual void Exec(TSharedPtr<FString> TargetContext, const TCHAR* Command) = 0;
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptComponent.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptComponent.h
new file mode 100644
index 0000000..679e0b1
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptComponent.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "JavascriptContext.h"
+#include "JavascriptComponent.generated.h"
+
+/**
+ * 
+ */
+UCLASS(BlueprintType, ClassGroup = Script, Blueprintable, hideCategories = (ComponentReplication), editinlinenew, meta = (BlueprintSpawnableComponent))
+class V8_API UJavascriptComponent : public UActorComponent
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FJavascriptTickSignature, float, DeltaSeconds);	
+	DECLARE_DYNAMIC_DELEGATE_OneParam(FJavascriptNameSignature, FName, Name);
+	DECLARE_DYNAMIC_DELEGATE(FJavascriptNoParamSignature);
+
+	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Javascript")
+	FString ScriptSourceFile;
+
+	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Javascript")
+	bool bActiveWithinEditor;
+
+	UPROPERTY(transient)
+	UJavascriptContext* JavascriptContext;	
+
+	UPROPERTY()
+	FJavascriptTickSignature OnTick;
+
+	UPROPERTY()
+	FJavascriptNoParamSignature OnBeginPlay;
+
+	UPROPERTY()
+	FJavascriptNoParamSignature OnEndPlay;
+
+	UPROPERTY()
+	FJavascriptNameSignature OnInvoke;
+
+	// Begin UActorComponent interface.
+	virtual void Activate(bool bReset = false) override;
+	virtual void Deactivate() override;	
+	virtual void OnRegister() override;
+	virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
+	virtual void BeginDestroy() override;
+	// Begin UActorComponent interface.
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	void ForceGC();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	void Expose(FString ExposedAs, UObject* Object);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript")
+	void Invoke(FName Name);
+
+	virtual void ProcessEvent(UFunction* Function, void* Parms) override;	
+
+	template <typename... Rest>
+	bool FastCall(Rest... rest)
+	{
+		return JavascriptContext && JavascriptContext->FastCall(this, rest...);
+	}
+
+	template <typename... Rest>
+	bool FastCallWithReturn(Rest... rest)
+	{
+		return JavascriptContext && JavascriptContext->FastCallWithReturn(this, rest...);
+	}
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptContext.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptContext.h
new file mode 100644
index 0000000..7280907
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptContext.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#include "JavascriptContext.generated.h"
+
+struct FJavascriptContext;
+class UJavascriptIsolate;
+
+struct V8_API FArrayBufferAccessor
+{	
+	static int32 GetSize();
+	static void* GetData();
+	static void Discard();
+};
+
+UCLASS()
+class V8_API UJavascriptContext : public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+	// Begin UObject interface.
+	static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
+	// End UObject interface.
+
+	TSharedPtr<FJavascriptContext> JavascriptContext;
+
+	TSharedPtr<FString> ContextId;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Scripting|Javascript")
+	TArray<FString> Paths;
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void SetContextId(FString Name);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void Expose(FString Name, UObject* Object);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	FString GetScriptFileFullPath(FString Filename);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	FString ReadScriptFile(FString Filename);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void RunFile(FString Filename);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	FString RunScript(FString Script, bool bOutput = true);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	bool WriteAliases(FString Target);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	bool WriteDTS(FString Target);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void SetAsDebugContext();
+
+	UFUNCTION(BlueprintPure, Category = "Scripting|Javascript")
+	bool IsDebugContext() const;
+
+	bool HasProxyFunction(UObject* Holder, UFunction* Function);
+	bool CallProxyFunction(UObject* Holder, UObject* This, UFunction* Function, void* Parms);
+
+	// FastCall
+	void InternalPushArgument(int32 Value);
+	void InternalPushArgument(float Value);
+	void InternalPushArgument(bool Value);
+	void InternalPushArgument(const FString& Value)
+	{
+		InternalPushArgument(*Value);
+	}
+	void InternalPushArgument(const TCHAR* Value);
+	void InternalPushArgument(UObject* Value);
+
+	bool InternalPopReturnValue(int32& Value);
+	bool InternalPopReturnValue(float& Value);
+	bool InternalPopReturnValue(bool& Value);
+	bool InternalPopReturnValue(FString& Value);
+	bool InternalPopReturnValue(UObject*& Value);
+
+	void InternalBegin();
+	void InternalEnd();
+	bool InternalCall(UObject* Object, FName Name);
+
+	template <typename First>
+	void InternalPushArguments(const First& first)
+	{
+		InternalPushArgument(first);
+	}
+
+	template <typename First, typename... Rest>
+	void InternalPushArguments(const First& first, Rest... rest)
+	{
+		InternalPushArgument(first);
+		InternalPushArguments(rest...);
+	}
+
+	template <typename... Rest>
+	bool FastCall(UObject* Object, FName Name, Rest... rest)
+	{
+		InternalBegin();
+		InternalPushArguments(rest...);
+		bool successful = InternalCall(Object, Name);
+		InternalEnd();
+		return successful;
+	}
+
+	template <typename Ret, typename... Rest>
+	bool FastCallWithReturn(UObject* Object, FName Name, Ret* ret, Rest... rest)
+	{
+		InternalBegin();
+		InternalPushArguments(rest...);
+		bool successful = InternalCall(Object, Name);
+		if (successful)
+		{
+			if (!InternalPopReturnValue(*ret))
+			{
+				successful = false;
+			}
+		}
+		InternalEnd();
+		return successful;
+	}
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptDelegate.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptDelegate.h
new file mode 100644
index 0000000..2a64c12
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptDelegate.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "JavascriptDelegate.generated.h"
+
+class FJavascriptDelegate;
+
+UCLASS()
+class V8_API UJavascriptDelegate : public UObject
+{
+	GENERATED_BODY()
+
+public:
+	int32 UniqueId;
+
+	FJavascriptDelegate* JavascriptDelegate;	
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	void Fire();
+
+	virtual void ProcessEvent(UFunction* Function, void* Parms) override;
+};
\ No newline at end of file
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedClass.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedClass.h
new file mode 100644
index 0000000..525fc6b
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedClass.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "JavascriptGeneratedClass.generated.h"
+
+struct FJavascriptContext;
+
+UCLASS()
+class V8_API UJavascriptGeneratedClass : public UBlueprintGeneratedClass
+{
+	GENERATED_BODY()
+
+public:		
+	TWeakPtr<FJavascriptContext> JavascriptContext;	
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedClass_Native.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedClass_Native.h
new file mode 100644
index 0000000..e31a806
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedClass_Native.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "JavascriptGeneratedClass_Native.generated.h"
+
+struct FJavascriptContext;
+
+UCLASS()
+class V8_API UJavascriptGeneratedClass_Native : public UBlueprintGeneratedClass
+{
+	GENERATED_BODY()
+
+public:
+	// UObject interface
+	virtual void Serialize(FArchive& Ar) override { UClass::Serialize(Ar);  }
+	virtual void PostLoad() override { UClass::PostLoad(); }
+	virtual void PostInitProperties() override { UClass::PostInitProperties(); }
+	// End UObject interface
+
+	// UClass interface
+#if WITH_EDITOR
+	virtual UClass* GetAuthoritativeClass() override { return UClass::GetAuthoritativeClass();  }
+	virtual void ConditionalRecompileClass(TArray<UObject*>* ObjLoaded) override { UClass::ConditionalRecompileClass(ObjLoaded);  }
+	virtual UObject* GetArchetypeForCDO() const override { return UClass::GetArchetypeForCDO();  }
+#endif //WITH_EDITOR
+	virtual bool IsFunctionImplementedInBlueprint(FName InFunctionName) const override { return false;  }
+	virtual uint8* GetPersistentUberGraphFrame(UObject* Obj, UFunction* FuncToCheck) const override { return nullptr;  }
+	virtual void CreatePersistentUberGraphFrame(UObject* Obj, bool bCreateOnlyIfEmpty = false, bool bSkipSuperClass = false) const override {}
+	virtual void DestroyPersistentUberGraphFrame(UObject* Obj, bool bSkipSuperClass = false) const override {}
+	virtual void Link(FArchive& Ar, bool bRelinkExistingProperties) override { UClass::Link(Ar, bRelinkExistingProperties); }
+	virtual void PurgeClass(bool bRecompilingOnLoad) override { UClass::PurgeClass(bRecompilingOnLoad);  }
+	virtual void Bind() override { UClass::Bind(); }
+	virtual void GetRequiredPreloadDependencies(TArray<UObject*>& DependenciesOut) override { UClass::GetRequiredPreloadDependencies(DependenciesOut);  }
+	virtual UObject* FindArchetype(UClass* ArchetypeClass, const FName ArchetypeName) const override { return UClass::FindArchetype(ArchetypeClass, ArchetypeName);  }
+	// End UClass interface
+
+public:		
+	TWeakPtr<FJavascriptContext> JavascriptContext;	
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedFunction.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedFunction.h
new file mode 100644
index 0000000..ddffb31
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptGeneratedFunction.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "JavascriptGeneratedFunction.generated.h"
+
+struct FJavascriptContext;
+
+UCLASS()
+class V8_API UJavascriptGeneratedFunction : public UFunction
+{
+	GENERATED_BODY()
+
+public:		
+	TWeakPtr<FJavascriptContext> JavascriptContext;	
+
+	DECLARE_FUNCTION(Thunk);
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptIsolate.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptIsolate.h
new file mode 100644
index 0000000..1b02aed
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptIsolate.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "JavascriptIsolate.generated.h"
+
+class UJavascriptContext;
+class FJavascriptIsolate;
+
+UCLASS()
+class V8_API UJavascriptIsolate : public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+	virtual ~UJavascriptIsolate() override;
+
+	TSharedPtr<FJavascriptIsolate> JavascriptIsolate;	
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	UJavascriptContext* CreateContext();
+
+	// Begin UObject interface.
+	static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
+	// End UObject interface.
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptLibrary.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptLibrary.h
new file mode 100644
index 0000000..5211b83
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptLibrary.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "JavascriptLibrary.generated.h"
+
+UCLASS()
+class V8_API UJavascriptLibrary : public UBlueprintFunctionLibrary
+{
+	GENERATED_BODY()
+
+public:	
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static void SetMobile(USceneComponent* SceneComponent);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static void SetMobility(USceneComponent* SceneComponent, EComponentMobility::Type Type);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UWorld* Actor_GetWorld(AActor* Actor);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UClass* GetBlueprintGeneratedClass(UBlueprint* Blueprint);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UClass* GetBlueprintGeneratedClassFromPath(FString Path);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UObject* GetOuter(UObject* Object);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UObject* GetOutermost(UObject* Object);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static FString GetName(UObject* Object);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static bool IsPlayInEditor(UWorld* World);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static bool IsPlayInPreview(UWorld* World);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static bool IsGameWorld(UWorld* World);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static void SetClientTravel(UEngine* Engine, UWorld *InWorld, FString NextURL, ETravelType InTravelType);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UPackage* CreatePackage(UObject* Outer, FString PackageName);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UActorComponent* FindComponentObjectByName(AActor* Actor, const FName& InName);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static void AddDynamicBinding(UClass* Outer, UDynamicBlueprintBinding* BindingObject);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static UDynamicBlueprintBinding* GetDynamicBinding(UClass* Outer, TSubclassOf<UDynamicBlueprintBinding> BindingObjectClass);
+
+	UFUNCTION(BlueprintCallable, Category = "Scripting|Javascript")
+	static void HandleSeamlessTravelPlayer(AGameMode* GameMode, AController*& C);
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptObject.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptObject.h
new file mode 100644
index 0000000..1fffa0b
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptObject.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "JavascriptObject.generated.h"
+
+UCLASS()
+class V8_API UJavascriptObject : public UObject
+{
+	GENERATED_BODY()
+
+public:		
+};
diff --git a/Plugins/UnrealJS/Source/V8/Public/JavascriptProcess.h b/Plugins/UnrealJS/Source/V8/Public/JavascriptProcess.h
new file mode 100644
index 0000000..bd2d64e
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/Public/JavascriptProcess.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "JavascriptProcess.generated.h"
+
+/**
+ * 
+ */
+UCLASS(BlueprintType)
+class V8_API UJavascriptProcess: public UObject
+{
+	GENERATED_UCLASS_BODY()
+
+public:
+	FProcHandle ProcessHandle;
+	uint32 ProcessID;
+	void* ReadPipe;
+	void* WritePipe;
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	static UJavascriptProcess* Create(const FString& URL, const FString& Parms, bool bLaunchDetached, bool bLaunchHidden, bool bLaunchReallyHidden, int32 PriorityModifier, const FString& OptionalWorkingDirectory, bool bUsePipe);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	bool IsRunning();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	void Close();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	void Wait();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	void Terminate(bool KillTree = false);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	bool GetReturnCode(int32& ReturnCode);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	FString ReadFromPipe();
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	bool ReadArrayFromPipe(TArray<uint8>& Array);
+
+	UFUNCTION(BlueprintCallable, Category = "Javascript | Scripting")
+	bool WriteToPipe(const FString& Message, FString& OutWritten);
+};
diff --git a/Plugins/UnrealJS/Source/V8/V8.Build.cs b/Plugins/UnrealJS/Source/V8/V8.Build.cs
new file mode 100644
index 0000000..8a32422
--- /dev/null
+++ b/Plugins/UnrealJS/Source/V8/V8.Build.cs
@@ -0,0 +1,106 @@
+using UnrealBuildTool;
+using System.IO;
+using System;
+
+public class V8 : ModuleRules
+{
+    protected string ModulePath
+    {
+        get { return Path.GetDirectoryName(RulesCompiler.GetModuleFilename(this.GetType().Name)); }
+    }
+
+    protected string ThirdPartyPath
+    {
+        get { return Path.GetFullPath(Path.Combine(ModulePath, "..", "..", "ThirdParty")); }
+    }
+
+    public V8(TargetInfo Target)
+    {
+        PrivateIncludePaths.AddRange(new string[] 
+        { 
+            Path.Combine(ThirdPartyPath, "v8", "include"),
+            Path.Combine(ThirdPartyPath, "v8"),
+            Path.Combine("V8", "Private") 
+        });
+        
+        PublicIncludePaths.AddRange(new string[] 
+        { 
+            Path.Combine("V8", "Public") 
+        });
+
+        PublicDependencyModuleNames.AddRange(new string[] 
+        { 
+            "Core", "CoreUObject", "Engine", "DirectoryWatcher"
+        });
+
+        PrivateDependencyModuleNames.AddRange(new string[] 
+        { 
+            "Sockets"
+        });
+
+        if (UEBuildConfiguration.bBuildEditor)
+        {
+            PrivateDependencyModuleNames.AddRange(new string[] 
+            { 
+                "UnrealEd"
+            });
+        }
+
+        bEnableExceptions = true;
+
+        LoadV8(Target);
+    }
+
+    private bool LoadV8(TargetInfo Target)
+    {
+        if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
+        {
+            string LibrariesPath = Path.Combine(ThirdPartyPath, "v8", "lib", "Windows");
+
+            if (WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2015)
+            {
+                LibrariesPath = Path.Combine(LibrariesPath, "MSVC2015");
+            }
+            else
+            {
+                LibrariesPath = Path.Combine(LibrariesPath, "MSVC2013");
+            }
+
+            if (Target.Platform == UnrealTargetPlatform.Win64)
+            {
+                LibrariesPath = Path.Combine(LibrariesPath, "x64");
+            }
+            else
+            {
+                
+                LibrariesPath = Path.Combine(LibrariesPath, "x86");
+            }
+
+            if (Target.Configuration == UnrealTargetConfiguration.Debug)
+            {
+                LibrariesPath = Path.Combine(LibrariesPath, "Debug");
+            }
+            else
+            {
+                LibrariesPath = Path.Combine(LibrariesPath, "Release");
+            }            
+
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_base_0.lib"));
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_base_1.lib"));
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_base_2.lib"));
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_base_3.lib"));
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_libbase.lib"));
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_libplatform.lib"));
+            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "v8_nosnapshot.lib"));
+
+            PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "v8", "Includes"));
+
+            Definitions.Add(string.Format("WITH_V8=1"));                       
+
+            return true;
+        }
+
+        Definitions.Add(string.Format("WITH_V8=0"));
+        return false;
+    }
+}
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/OWNERS b/Plugins/UnrealJS/ThirdParty/v8/include/OWNERS
new file mode 100644
index 0000000..efa3b93
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/OWNERS
@@ -0,0 +1,2 @@
+danno@chromium.org
+jochen@chromium.org
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/libplatform/libplatform.h b/Plugins/UnrealJS/ThirdParty/v8/include/libplatform/libplatform.h
new file mode 100644
index 0000000..2125e97
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/libplatform/libplatform.h
@@ -0,0 +1,38 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_LIBPLATFORM_LIBPLATFORM_H_
+#define V8_LIBPLATFORM_LIBPLATFORM_H_
+
+#include "include/v8-platform.h"
+
+namespace v8 {
+namespace platform {
+
+/**
+ * Returns a new instance of the default v8::Platform implementation.
+ *
+ * The caller will take ownership of the returned pointer. |thread_pool_size|
+ * is the number of worker threads to allocate for background jobs. If a value
+ * of zero is passed, a suitable default based on the current number of
+ * processors online will be chosen.
+ */
+v8::Platform* CreateDefaultPlatform(int thread_pool_size = 0);
+
+
+/**
+ * Pumps the message loop for the given isolate.
+ *
+ * The caller has to make sure that this is called from the right thread.
+ * Returns true if a task was executed, and false otherwise. This call does
+ * not block if no task is pending. The |platform| has to be created using
+ * |CreateDefaultPlatform|.
+ */
+bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate);
+
+
+}  // namespace platform
+}  // namespace v8
+
+#endif  // V8_LIBPLATFORM_LIBPLATFORM_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8-debug.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8-debug.h
new file mode 100644
index 0000000..0b64fb3
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8-debug.h
@@ -0,0 +1,280 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_V8_DEBUG_H_
+#define V8_V8_DEBUG_H_
+
+#include "v8.h"  // NOLINT(build/include)
+
+/**
+ * Debugger support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+// Debug events which can occur in the V8 JavaScript engine.
+enum DebugEvent {
+  Break = 1,
+  Exception = 2,
+  NewFunction = 3,
+  BeforeCompile = 4,
+  AfterCompile  = 5,
+  CompileError = 6,
+  PromiseEvent = 7,
+  AsyncTaskEvent = 8,
+};
+
+
+class V8_EXPORT Debug {
+ public:
+  /**
+   * A client object passed to the v8 debugger whose ownership will be taken by
+   * it. v8 is always responsible for deleting the object.
+   */
+  class ClientData {
+   public:
+    virtual ~ClientData() {}
+  };
+
+
+  /**
+   * A message object passed to the debug message handler.
+   */
+  class Message {
+   public:
+    /**
+     * Check type of message.
+     */
+    virtual bool IsEvent() const = 0;
+    virtual bool IsResponse() const = 0;
+    virtual DebugEvent GetEvent() const = 0;
+
+    /**
+     * Indicate whether this is a response to a continue command which will
+     * start the VM running after this is processed.
+     */
+    virtual bool WillStartRunning() const = 0;
+
+    /**
+     * Access to execution state and event data. Don't store these cross
+     * callbacks as their content becomes invalid. These objects are from the
+     * debugger event that started the debug message loop.
+     */
+    virtual Local<Object> GetExecutionState() const = 0;
+    virtual Local<Object> GetEventData() const = 0;
+
+    /**
+     * Get the debugger protocol JSON.
+     */
+    virtual Local<String> GetJSON() const = 0;
+
+    /**
+     * Get the context active when the debug event happened. Note this is not
+     * the current active context as the JavaScript part of the debugger is
+     * running in its own context which is entered at this point.
+     */
+    virtual Local<Context> GetEventContext() const = 0;
+
+    /**
+     * Client data passed with the corresponding request if any. This is the
+     * client_data data value passed into Debug::SendCommand along with the
+     * request that led to the message or NULL if the message is an event. The
+     * debugger takes ownership of the data and will delete it even if there is
+     * no message handler.
+     */
+    virtual ClientData* GetClientData() const = 0;
+
+    virtual Isolate* GetIsolate() const = 0;
+
+    virtual ~Message() {}
+  };
+
+
+  /**
+   * An event details object passed to the debug event listener.
+   */
+  class EventDetails {
+   public:
+    /**
+     * Event type.
+     */
+    virtual DebugEvent GetEvent() const = 0;
+
+    /**
+     * Access to execution state and event data of the debug event. Don't store
+     * these cross callbacks as their content becomes invalid.
+     */
+    virtual Local<Object> GetExecutionState() const = 0;
+    virtual Local<Object> GetEventData() const = 0;
+
+    /**
+     * Get the context active when the debug event happened. Note this is not
+     * the current active context as the JavaScript part of the debugger is
+     * running in its own context which is entered at this point.
+     */
+    virtual Local<Context> GetEventContext() const = 0;
+
+    /**
+     * Client data passed with the corresponding callback when it was
+     * registered.
+     */
+    virtual Local<Value> GetCallbackData() const = 0;
+
+    /**
+     * Client data passed to DebugBreakForCommand function. The
+     * debugger takes ownership of the data and will delete it even if
+     * there is no message handler.
+     */
+    virtual ClientData* GetClientData() const = 0;
+
+    virtual ~EventDetails() {}
+  };
+
+  /**
+   * Debug event callback function.
+   *
+   * \param event_details object providing information about the debug event
+   *
+   * A EventCallback2 does not take possession of the event data,
+   * and must not rely on the data persisting after the handler returns.
+   */
+  typedef void (*EventCallback)(const EventDetails& event_details);
+
+  /**
+   * Debug message callback function.
+   *
+   * \param message the debug message handler message object
+   *
+   * A MessageHandler2 does not take possession of the message data,
+   * and must not rely on the data persisting after the handler returns.
+   */
+  typedef void (*MessageHandler)(const Message& message);
+
+  /**
+   * Callback function for the host to ensure debug messages are processed.
+   */
+  typedef void (*DebugMessageDispatchHandler)();
+
+  static bool SetDebugEventListener(EventCallback that,
+                                    Local<Value> data = Local<Value>());
+
+  // Schedule a debugger break to happen when JavaScript code is run
+  // in the given isolate.
+  static void DebugBreak(Isolate* isolate);
+
+  // Remove scheduled debugger break in given isolate if it has not
+  // happened yet.
+  static void CancelDebugBreak(Isolate* isolate);
+
+  // Check if a debugger break is scheduled in the given isolate.
+  static bool CheckDebugBreak(Isolate* isolate);
+
+  // Message based interface. The message protocol is JSON.
+  static void SetMessageHandler(MessageHandler handler);
+
+  static void SendCommand(Isolate* isolate,
+                          const uint16_t* command, int length,
+                          ClientData* client_data = NULL);
+
+ /**
+  * Run a JavaScript function in the debugger.
+  * \param fun the function to call
+  * \param data passed as second argument to the function
+  * With this call the debugger is entered and the function specified is called
+  * with the execution state as the first argument. This makes it possible to
+  * get access to information otherwise not available during normal JavaScript
+  * execution e.g. details on stack frames. Receiver of the function call will
+  * be the debugger context global object, however this is a subject to change.
+  * The following example shows a JavaScript function which when passed to
+  * v8::Debug::Call will return the current line of JavaScript execution.
+  *
+  * \code
+  *   function frame_source_line(exec_state) {
+  *     return exec_state.frame(0).sourceLine();
+  *   }
+  * \endcode
+  */
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<Value> Call(v8::Local<v8::Function> fun,
+                        Local<Value> data = Local<Value>()));
+  // TODO(dcarney): data arg should be a MaybeLocal
+  static MaybeLocal<Value> Call(Local<Context> context,
+                                v8::Local<v8::Function> fun,
+                                Local<Value> data = Local<Value>());
+
+  /**
+   * Returns a mirror object for the given object.
+   */
+  static V8_DEPRECATE_SOON("Use maybe version",
+                           Local<Value> GetMirror(v8::Local<v8::Value> obj));
+  static MaybeLocal<Value> GetMirror(Local<Context> context,
+                                     v8::Local<v8::Value> obj);
+
+  /**
+   * Makes V8 process all pending debug messages.
+   *
+   * From V8 point of view all debug messages come asynchronously (e.g. from
+   * remote debugger) but they all must be handled synchronously: V8 cannot
+   * do 2 things at one time so normal script execution must be interrupted
+   * for a while.
+   *
+   * Generally when message arrives V8 may be in one of 3 states:
+   * 1. V8 is running script; V8 will automatically interrupt and process all
+   * pending messages;
+   * 2. V8 is suspended on debug breakpoint; in this state V8 is dedicated
+   * to reading and processing debug messages;
+   * 3. V8 is not running at all or has called some long-working C++ function;
+   * by default it means that processing of all debug messages will be deferred
+   * until V8 gets control again; however, embedding application may improve
+   * this by manually calling this method.
+   *
+   * Technically this method in many senses is equivalent to executing empty
+   * script:
+   * 1. It does nothing except for processing all pending debug messages.
+   * 2. It should be invoked with the same precautions and from the same context
+   * as V8 script would be invoked from, because:
+   *   a. with "evaluate" command it can do whatever normal script can do,
+   *   including all native calls;
+   *   b. no other thread should call V8 while this method is running
+   *   (v8::Locker may be used here).
+   *
+   * "Evaluate" debug command behavior currently is not specified in scope
+   * of this method.
+   */
+  static void ProcessDebugMessages();
+
+  /**
+   * Debugger is running in its own context which is entered while debugger
+   * messages are being dispatched. This is an explicit getter for this
+   * debugger context. Note that the content of the debugger context is subject
+   * to change. The Context exists only when the debugger is active, i.e. at
+   * least one DebugEventListener or MessageHandler is set.
+   */
+  static Local<Context> GetDebugContext();
+
+
+  /**
+   * Enable/disable LiveEdit functionality for the given Isolate
+   * (default Isolate if not provided). V8 will abort if LiveEdit is
+   * unexpectedly used. LiveEdit is enabled by default.
+   */
+  static void SetLiveEditEnabled(Isolate* isolate, bool enable);
+
+  /**
+   * Returns array of internal properties specific to the value type. Result has
+   * the following format: [<name>, <value>,...,<name>, <value>]. Result array
+   * will be allocated in the current context.
+   */
+  static MaybeLocal<Array> GetInternalProperties(Isolate* isolate,
+                                                 Local<Value> value);
+};
+
+
+}  // namespace v8
+
+
+#undef EXPORT
+
+
+#endif  // V8_V8_DEBUG_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8-platform.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8-platform.h
new file mode 100644
index 0000000..c6cba0f
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8-platform.h
@@ -0,0 +1,114 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_V8_PLATFORM_H_
+#define V8_V8_PLATFORM_H_
+
+namespace v8 {
+
+class Isolate;
+
+/**
+ * A Task represents a unit of work.
+ */
+class Task {
+ public:
+  virtual ~Task() {}
+
+  virtual void Run() = 0;
+};
+
+
+/**
+* An IdleTask represents a unit of work to be performed in idle time.
+* The Run method is invoked with an argument that specifies the deadline in
+* seconds returned by MonotonicallyIncreasingTime().
+* The idle task is expected to complete by this deadline.
+*/
+class IdleTask {
+ public:
+  virtual ~IdleTask() {}
+  virtual void Run(double deadline_in_seconds) = 0;
+};
+
+
+/**
+ * V8 Platform abstraction layer.
+ *
+ * The embedder has to provide an implementation of this interface before
+ * initializing the rest of V8.
+ */
+class Platform {
+ public:
+  /**
+   * This enum is used to indicate whether a task is potentially long running,
+   * or causes a long wait. The embedder might want to use this hint to decide
+   * whether to execute the task on a dedicated thread.
+   */
+  enum ExpectedRuntime {
+    kShortRunningTask,
+    kLongRunningTask
+  };
+
+  virtual ~Platform() {}
+
+  /**
+   * Schedules a task to be invoked on a background thread. |expected_runtime|
+   * indicates that the task will run a long time. The Platform implementation
+   * takes ownership of |task|. There is no guarantee about order of execution
+   * of tasks wrt order of scheduling, nor is there a guarantee about the
+   * thread the task will be run on.
+   */
+  virtual void CallOnBackgroundThread(Task* task,
+                                      ExpectedRuntime expected_runtime) = 0;
+
+  /**
+   * Schedules a task to be invoked on a foreground thread wrt a specific
+   * |isolate|. Tasks posted for the same isolate should be execute in order of
+   * scheduling. The definition of "foreground" is opaque to V8.
+   */
+  virtual void CallOnForegroundThread(Isolate* isolate, Task* task) = 0;
+
+  /**
+   * Schedules a task to be invoked on a foreground thread wrt a specific
+   * |isolate| after the given number of seconds |delay_in_seconds|.
+   * Tasks posted for the same isolate should be execute in order of
+   * scheduling. The definition of "foreground" is opaque to V8.
+   */
+  virtual void CallDelayedOnForegroundThread(Isolate* isolate, Task* task,
+                                             double delay_in_seconds) = 0;
+
+  /**
+   * Schedules a task to be invoked on a foreground thread wrt a specific
+   * |isolate| when the embedder is idle.
+   * Requires that SupportsIdleTasks(isolate) is true.
+   * Idle tasks may be reordered relative to other task types and may be
+   * starved for an arbitrarily long time if no idle time is available.
+   * The definition of "foreground" is opaque to V8.
+   */
+  virtual void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) {
+    // TODO(ulan): Make this function abstract after V8 roll in Chromium.
+  }
+
+  /**
+   * Returns true if idle tasks are enabled for the given |isolate|.
+   */
+  virtual bool IdleTasksEnabled(Isolate* isolate) {
+    // TODO(ulan): Make this function abstract after V8 roll in Chromium.
+    return false;
+  }
+
+  /**
+   * Monotonically increasing time in seconds from an arbitrary fixed point in
+   * the past. This function is expected to return at least
+   * millisecond-precision values. For this reason,
+   * it is recommended that the fixed point be no further in the past than
+   * the epoch.
+   **/
+  virtual double MonotonicallyIncreasingTime() = 0;
+};
+
+}  // namespace v8
+
+#endif  // V8_V8_PLATFORM_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8-profiler.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8-profiler.h
new file mode 100644
index 0000000..e432600
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8-profiler.h
@@ -0,0 +1,648 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_V8_PROFILER_H_
+#define V8_V8_PROFILER_H_
+
+#include <vector>
+#include "v8.h"  // NOLINT(build/include)
+
+/**
+ * Profiler support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+class HeapGraphNode;
+struct HeapStatsUpdate;
+
+typedef uint32_t SnapshotObjectId;
+
+
+struct CpuProfileDeoptFrame {
+  int script_id;
+  size_t position;
+};
+
+}  // namespace v8
+
+#ifdef V8_OS_WIN
+template class V8_EXPORT std::vector<v8::CpuProfileDeoptFrame>;
+#endif
+
+namespace v8 {
+
+struct V8_EXPORT CpuProfileDeoptInfo {
+  /** A pointer to a static string owned by v8. */
+  const char* deopt_reason;
+  std::vector<CpuProfileDeoptFrame> stack;
+};
+
+}  // namespace v8
+
+#ifdef V8_OS_WIN
+template class V8_EXPORT std::vector<v8::CpuProfileDeoptInfo>;
+#endif
+
+namespace v8 {
+
+/**
+ * CpuProfileNode represents a node in a call graph.
+ */
+class V8_EXPORT CpuProfileNode {
+ public:
+  struct LineTick {
+    /** The 1-based number of the source line where the function originates. */
+    int line;
+
+    /** The count of samples associated with the source line. */
+    unsigned int hit_count;
+  };
+
+  /** Returns function name (empty string for anonymous functions.) */
+  Local<String> GetFunctionName() const;
+
+  /** Returns id of the script where function is located. */
+  int GetScriptId() const;
+
+  /** Returns resource name for script from where the function originates. */
+  Local<String> GetScriptResourceName() const;
+
+  /**
+   * Returns the number, 1-based, of the line where the function originates.
+   * kNoLineNumberInfo if no line number information is available.
+   */
+  int GetLineNumber() const;
+
+  /**
+   * Returns 1-based number of the column where the function originates.
+   * kNoColumnNumberInfo if no column number information is available.
+   */
+  int GetColumnNumber() const;
+
+  /**
+   * Returns the number of the function's source lines that collect the samples.
+   */
+  unsigned int GetHitLineCount() const;
+
+  /** Returns the set of source lines that collect the samples.
+   *  The caller allocates buffer and responsible for releasing it.
+   *  True if all available entries are copied, otherwise false.
+   *  The function copies nothing if buffer is not large enough.
+   */
+  bool GetLineTicks(LineTick* entries, unsigned int length) const;
+
+  /** Returns bailout reason for the function
+    * if the optimization was disabled for it.
+    */
+  const char* GetBailoutReason() const;
+
+  /**
+    * Returns the count of samples where the function was currently executing.
+    */
+  unsigned GetHitCount() const;
+
+  /** Returns function entry UID. */
+  unsigned GetCallUid() const;
+
+  /** Returns id of the node. The id is unique within the tree */
+  unsigned GetNodeId() const;
+
+  /** Returns child nodes count of the node. */
+  int GetChildrenCount() const;
+
+  /** Retrieves a child node by index. */
+  const CpuProfileNode* GetChild(int index) const;
+
+  /** Retrieves deopt infos for the node. */
+  const std::vector<CpuProfileDeoptInfo>& GetDeoptInfos() const;
+
+  static const int kNoLineNumberInfo = Message::kNoLineNumberInfo;
+  static const int kNoColumnNumberInfo = Message::kNoColumnInfo;
+};
+
+
+/**
+ * CpuProfile contains a CPU profile in a form of top-down call tree
+ * (from main() down to functions that do all the work).
+ */
+class V8_EXPORT CpuProfile {
+ public:
+  /** Returns CPU profile title. */
+  Local<String> GetTitle() const;
+
+  /** Returns the root node of the top down call tree. */
+  const CpuProfileNode* GetTopDownRoot() const;
+
+  /**
+   * Returns number of samples recorded. The samples are not recorded unless
+   * |record_samples| parameter of CpuProfiler::StartCpuProfiling is true.
+   */
+  int GetSamplesCount() const;
+
+  /**
+   * Returns profile node corresponding to the top frame the sample at
+   * the given index.
+   */
+  const CpuProfileNode* GetSample(int index) const;
+
+  /**
+   * Returns the timestamp of the sample. The timestamp is the number of
+   * microseconds since some unspecified starting point.
+   * The point is equal to the starting point used by GetStartTime.
+   */
+  int64_t GetSampleTimestamp(int index) const;
+
+  /**
+   * Returns time when the profile recording was started (in microseconds)
+   * since some unspecified starting point.
+   */
+  int64_t GetStartTime() const;
+
+  /**
+   * Returns time when the profile recording was stopped (in microseconds)
+   * since some unspecified starting point.
+   * The point is equal to the starting point used by GetStartTime.
+   */
+  int64_t GetEndTime() const;
+
+  /**
+   * Deletes the profile and removes it from CpuProfiler's list.
+   * All pointers to nodes previously returned become invalid.
+   */
+  void Delete();
+};
+
+
+/**
+ * Interface for controlling CPU profiling. Instance of the
+ * profiler can be retrieved using v8::Isolate::GetCpuProfiler.
+ */
+class V8_EXPORT CpuProfiler {
+ public:
+  /**
+   * Changes default CPU profiler sampling interval to the specified number
+   * of microseconds. Default interval is 1000us. This method must be called
+   * when there are no profiles being recorded.
+   */
+  void SetSamplingInterval(int us);
+
+  /**
+   * Starts collecting CPU profile. Title may be an empty string. It
+   * is allowed to have several profiles being collected at
+   * once. Attempts to start collecting several profiles with the same
+   * title are silently ignored. While collecting a profile, functions
+   * from all security contexts are included in it. The token-based
+   * filtering is only performed when querying for a profile.
+   *
+   * |record_samples| parameter controls whether individual samples should
+   * be recorded in addition to the aggregated tree.
+   */
+  void StartProfiling(Local<String> title, bool record_samples = false);
+
+  /**
+   * Stops collecting CPU profile with a given title and returns it.
+   * If the title given is empty, finishes the last profile started.
+   */
+  CpuProfile* StopProfiling(Local<String> title);
+
+  /**
+   * Tells the profiler whether the embedder is idle.
+   */
+  void SetIdle(bool is_idle);
+
+ private:
+  CpuProfiler();
+  ~CpuProfiler();
+  CpuProfiler(const CpuProfiler&);
+  CpuProfiler& operator=(const CpuProfiler&);
+};
+
+
+/**
+ * HeapSnapshotEdge represents a directed connection between heap
+ * graph nodes: from retainers to retained nodes.
+ */
+class V8_EXPORT HeapGraphEdge {
+ public:
+  enum Type {
+    kContextVariable = 0,  // A variable from a function context.
+    kElement = 1,          // An element of an array.
+    kProperty = 2,         // A named object property.
+    kInternal = 3,         // A link that can't be accessed from JS,
+                           // thus, its name isn't a real property name
+                           // (e.g. parts of a ConsString).
+    kHidden = 4,           // A link that is needed for proper sizes
+                           // calculation, but may be hidden from user.
+    kShortcut = 5,         // A link that must not be followed during
+                           // sizes calculation.
+    kWeak = 6              // A weak reference (ignored by the GC).
+  };
+
+  /** Returns edge type (see HeapGraphEdge::Type). */
+  Type GetType() const;
+
+  /**
+   * Returns edge name. This can be a variable name, an element index, or
+   * a property name.
+   */
+  Local<Value> GetName() const;
+
+  /** Returns origin node. */
+  const HeapGraphNode* GetFromNode() const;
+
+  /** Returns destination node. */
+  const HeapGraphNode* GetToNode() const;
+};
+
+
+/**
+ * HeapGraphNode represents a node in a heap graph.
+ */
+class V8_EXPORT HeapGraphNode {
+ public:
+  enum Type {
+    kHidden = 0,         // Hidden node, may be filtered when shown to user.
+    kArray = 1,          // An array of elements.
+    kString = 2,         // A string.
+    kObject = 3,         // A JS object (except for arrays and strings).
+    kCode = 4,           // Compiled code.
+    kClosure = 5,        // Function closure.
+    kRegExp = 6,         // RegExp.
+    kHeapNumber = 7,     // Number stored in the heap.
+    kNative = 8,         // Native object (not from V8 heap).
+    kSynthetic = 9,      // Synthetic object, usualy used for grouping
+                         // snapshot items together.
+    kConsString = 10,    // Concatenated string. A pair of pointers to strings.
+    kSlicedString = 11,  // Sliced string. A fragment of another string.
+    kSymbol = 12,        // A Symbol (ES6).
+    kSimdValue = 13      // A SIMD value stored in the heap (Proposed ES7).
+  };
+
+  /** Returns node type (see HeapGraphNode::Type). */
+  Type GetType() const;
+
+  /**
+   * Returns node name. Depending on node's type this can be the name
+   * of the constructor (for objects), the name of the function (for
+   * closures), string value, or an empty string (for compiled code).
+   */
+  Local<String> GetName() const;
+
+  /**
+   * Returns node id. For the same heap object, the id remains the same
+   * across all snapshots.
+   */
+  SnapshotObjectId GetId() const;
+
+  /** Returns node's own size, in bytes. */
+  size_t GetShallowSize() const;
+
+  /** Returns child nodes count of the node. */
+  int GetChildrenCount() const;
+
+  /** Retrieves a child by index. */
+  const HeapGraphEdge* GetChild(int index) const;
+};
+
+
+/**
+ * An interface for exporting data from V8, using "push" model.
+ */
+class V8_EXPORT OutputStream {  // NOLINT
+ public:
+  enum WriteResult {
+    kContinue = 0,
+    kAbort = 1
+  };
+  virtual ~OutputStream() {}
+  /** Notify about the end of stream. */
+  virtual void EndOfStream() = 0;
+  /** Get preferred output chunk size. Called only once. */
+  virtual int GetChunkSize() { return 1024; }
+  /**
+   * Writes the next chunk of snapshot data into the stream. Writing
+   * can be stopped by returning kAbort as function result. EndOfStream
+   * will not be called in case writing was aborted.
+   */
+  virtual WriteResult WriteAsciiChunk(char* data, int size) = 0;
+  /**
+   * Writes the next chunk of heap stats data into the stream. Writing
+   * can be stopped by returning kAbort as function result. EndOfStream
+   * will not be called in case writing was aborted.
+   */
+  virtual WriteResult WriteHeapStatsChunk(HeapStatsUpdate* data, int count) {
+    return kAbort;
+  }
+};
+
+
+/**
+ * HeapSnapshots record the state of the JS heap at some moment.
+ */
+class V8_EXPORT HeapSnapshot {
+ public:
+  enum SerializationFormat {
+    kJSON = 0  // See format description near 'Serialize' method.
+  };
+
+  /** Returns the root node of the heap graph. */
+  const HeapGraphNode* GetRoot() const;
+
+  /** Returns a node by its id. */
+  const HeapGraphNode* GetNodeById(SnapshotObjectId id) const;
+
+  /** Returns total nodes count in the snapshot. */
+  int GetNodesCount() const;
+
+  /** Returns a node by index. */
+  const HeapGraphNode* GetNode(int index) const;
+
+  /** Returns a max seen JS object Id. */
+  SnapshotObjectId GetMaxSnapshotJSObjectId() const;
+
+  /**
+   * Deletes the snapshot and removes it from HeapProfiler's list.
+   * All pointers to nodes, edges and paths previously returned become
+   * invalid.
+   */
+  void Delete();
+
+  /**
+   * Prepare a serialized representation of the snapshot. The result
+   * is written into the stream provided in chunks of specified size.
+   * The total length of the serialized snapshot is unknown in
+   * advance, it can be roughly equal to JS heap size (that means,
+   * it can be really big - tens of megabytes).
+   *
+   * For the JSON format, heap contents are represented as an object
+   * with the following structure:
+   *
+   *  {
+   *    snapshot: {
+   *      title: "...",
+   *      uid: nnn,
+   *      meta: { meta-info },
+   *      node_count: nnn,
+   *      edge_count: nnn
+   *    },
+   *    nodes: [nodes array],
+   *    edges: [edges array],
+   *    strings: [strings array]
+   *  }
+   *
+   * Nodes reference strings, other nodes, and edges by their indexes
+   * in corresponding arrays.
+   */
+  void Serialize(OutputStream* stream,
+                 SerializationFormat format = kJSON) const;
+};
+
+
+/**
+ * An interface for reporting progress and controlling long-running
+ * activities.
+ */
+class V8_EXPORT ActivityControl {  // NOLINT
+ public:
+  enum ControlOption {
+    kContinue = 0,
+    kAbort = 1
+  };
+  virtual ~ActivityControl() {}
+  /**
+   * Notify about current progress. The activity can be stopped by
+   * returning kAbort as the callback result.
+   */
+  virtual ControlOption ReportProgressValue(int done, int total) = 0;
+};
+
+
+/**
+ * Interface for controlling heap profiling. Instance of the
+ * profiler can be retrieved using v8::Isolate::GetHeapProfiler.
+ */
+class V8_EXPORT HeapProfiler {
+ public:
+  /**
+   * Callback function invoked for obtaining RetainedObjectInfo for
+   * the given JavaScript wrapper object. It is prohibited to enter V8
+   * while the callback is running: only getters on the handle and
+   * GetPointerFromInternalField on the objects are allowed.
+   */
+  typedef RetainedObjectInfo* (*WrapperInfoCallback)(uint16_t class_id,
+                                                     Local<Value> wrapper);
+
+  /** Returns the number of snapshots taken. */
+  int GetSnapshotCount();
+
+  /** Returns a snapshot by index. */
+  const HeapSnapshot* GetHeapSnapshot(int index);
+
+  /**
+   * Returns SnapshotObjectId for a heap object referenced by |value| if
+   * it has been seen by the heap profiler, kUnknownObjectId otherwise.
+   */
+  SnapshotObjectId GetObjectId(Local<Value> value);
+
+  /**
+   * Returns heap object with given SnapshotObjectId if the object is alive,
+   * otherwise empty handle is returned.
+   */
+  Local<Value> FindObjectById(SnapshotObjectId id);
+
+  /**
+   * Clears internal map from SnapshotObjectId to heap object. The new objects
+   * will not be added into it unless a heap snapshot is taken or heap object
+   * tracking is kicked off.
+   */
+  void ClearObjectIds();
+
+  /**
+   * A constant for invalid SnapshotObjectId. GetSnapshotObjectId will return
+   * it in case heap profiler cannot find id  for the object passed as
+   * parameter. HeapSnapshot::GetNodeById will always return NULL for such id.
+   */
+  static const SnapshotObjectId kUnknownObjectId = 0;
+
+  /**
+   * Callback interface for retrieving user friendly names of global objects.
+   */
+  class ObjectNameResolver {
+   public:
+    /**
+     * Returns name to be used in the heap snapshot for given node. Returned
+     * string must stay alive until snapshot collection is completed.
+     */
+    virtual const char* GetName(Local<Object> object) = 0;
+
+   protected:
+    virtual ~ObjectNameResolver() {}
+  };
+
+  /**
+   * Takes a heap snapshot and returns it.
+   */
+  const HeapSnapshot* TakeHeapSnapshot(
+      ActivityControl* control = NULL,
+      ObjectNameResolver* global_object_name_resolver = NULL);
+
+  /**
+   * Starts tracking of heap objects population statistics. After calling
+   * this method, all heap objects relocations done by the garbage collector
+   * are being registered.
+   *
+   * |track_allocations| parameter controls whether stack trace of each
+   * allocation in the heap will be recorded and reported as part of
+   * HeapSnapshot.
+   */
+  void StartTrackingHeapObjects(bool track_allocations = false);
+
+  /**
+   * Adds a new time interval entry to the aggregated statistics array. The
+   * time interval entry contains information on the current heap objects
+   * population size. The method also updates aggregated statistics and
+   * reports updates for all previous time intervals via the OutputStream
+   * object. Updates on each time interval are provided as a stream of the
+   * HeapStatsUpdate structure instances.
+   * If |timestamp_us| is supplied, timestamp of the new entry will be written
+   * into it. The return value of the function is the last seen heap object Id.
+   *
+   * StartTrackingHeapObjects must be called before the first call to this
+   * method.
+   */
+  SnapshotObjectId GetHeapStats(OutputStream* stream,
+                                int64_t* timestamp_us = NULL);
+
+  /**
+   * Stops tracking of heap objects population statistics, cleans up all
+   * collected data. StartHeapObjectsTracking must be called again prior to
+   * calling GetHeapStats next time.
+   */
+  void StopTrackingHeapObjects();
+
+  /**
+   * Deletes all snapshots taken. All previously returned pointers to
+   * snapshots and their contents become invalid after this call.
+   */
+  void DeleteAllHeapSnapshots();
+
+  /** Binds a callback to embedder's class ID. */
+  void SetWrapperClassInfoProvider(
+      uint16_t class_id,
+      WrapperInfoCallback callback);
+
+  /**
+   * Default value of persistent handle class ID. Must not be used to
+   * define a class. Can be used to reset a class of a persistent
+   * handle.
+   */
+  static const uint16_t kPersistentHandleNoClassId = 0;
+
+  /** Returns memory used for profiler internal data and snapshots. */
+  size_t GetProfilerMemorySize();
+
+  /**
+   * Sets a RetainedObjectInfo for an object group (see V8::SetObjectGroupId).
+   */
+  void SetRetainedObjectInfo(UniqueId id, RetainedObjectInfo* info);
+
+ private:
+  HeapProfiler();
+  ~HeapProfiler();
+  HeapProfiler(const HeapProfiler&);
+  HeapProfiler& operator=(const HeapProfiler&);
+};
+
+
+/**
+ * Interface for providing information about embedder's objects
+ * held by global handles. This information is reported in two ways:
+ *
+ *  1. When calling AddObjectGroup, an embedder may pass
+ *     RetainedObjectInfo instance describing the group.  To collect
+ *     this information while taking a heap snapshot, V8 calls GC
+ *     prologue and epilogue callbacks.
+ *
+ *  2. When a heap snapshot is collected, V8 additionally
+ *     requests RetainedObjectInfos for persistent handles that
+ *     were not previously reported via AddObjectGroup.
+ *
+ * Thus, if an embedder wants to provide information about native
+ * objects for heap snapshots, he can do it in a GC prologue
+ * handler, and / or by assigning wrapper class ids in the following way:
+ *
+ *  1. Bind a callback to class id by calling SetWrapperClassInfoProvider.
+ *  2. Call SetWrapperClassId on certain persistent handles.
+ *
+ * V8 takes ownership of RetainedObjectInfo instances passed to it and
+ * keeps them alive only during snapshot collection. Afterwards, they
+ * are freed by calling the Dispose class function.
+ */
+class V8_EXPORT RetainedObjectInfo {  // NOLINT
+ public:
+  /** Called by V8 when it no longer needs an instance. */
+  virtual void Dispose() = 0;
+
+  /** Returns whether two instances are equivalent. */
+  virtual bool IsEquivalent(RetainedObjectInfo* other) = 0;
+
+  /**
+   * Returns hash value for the instance. Equivalent instances
+   * must have the same hash value.
+   */
+  virtual intptr_t GetHash() = 0;
+
+  /**
+   * Returns human-readable label. It must be a null-terminated UTF-8
+   * encoded string. V8 copies its contents during a call to GetLabel.
+   */
+  virtual const char* GetLabel() = 0;
+
+  /**
+   * Returns human-readable group label. It must be a null-terminated UTF-8
+   * encoded string. V8 copies its contents during a call to GetGroupLabel.
+   * Heap snapshot generator will collect all the group names, create
+   * top level entries with these names and attach the objects to the
+   * corresponding top level group objects. There is a default
+   * implementation which is required because embedders don't have their
+   * own implementation yet.
+   */
+  virtual const char* GetGroupLabel() { return GetLabel(); }
+
+  /**
+   * Returns element count in case if a global handle retains
+   * a subgraph by holding one of its nodes.
+   */
+  virtual intptr_t GetElementCount() { return -1; }
+
+  /** Returns embedder's object size in bytes. */
+  virtual intptr_t GetSizeInBytes() { return -1; }
+
+ protected:
+  RetainedObjectInfo() {}
+  virtual ~RetainedObjectInfo() {}
+
+ private:
+  RetainedObjectInfo(const RetainedObjectInfo&);
+  RetainedObjectInfo& operator=(const RetainedObjectInfo&);
+};
+
+
+/**
+ * A struct for exporting HeapStats data from V8, using "push" model.
+ * See HeapProfiler::GetHeapStats.
+ */
+struct HeapStatsUpdate {
+  HeapStatsUpdate(uint32_t index, uint32_t count, uint32_t size)
+    : index(index), count(count), size(size) { }
+  uint32_t index;  // Index of the time interval that was changed.
+  uint32_t count;  // New value of count field for the interval with this index.
+  uint32_t size;  // New value of size field for the interval with this index.
+};
+
+
+}  // namespace v8
+
+
+#endif  // V8_V8_PROFILER_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8-testing.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8-testing.h
new file mode 100644
index 0000000..d18fc72
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8-testing.h
@@ -0,0 +1,48 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_V8_TEST_H_
+#define V8_V8_TEST_H_
+
+#include "v8.h"  // NOLINT(build/include)
+
+/**
+ * Testing support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+class V8_EXPORT Testing {
+ public:
+  enum StressType {
+    kStressTypeOpt,
+    kStressTypeDeopt
+  };
+
+  /**
+   * Set the type of stressing to do. The default if not set is kStressTypeOpt.
+   */
+  static void SetStressRunType(StressType type);
+
+  /**
+   * Get the number of runs of a given test that is required to get the full
+   * stress coverage.
+   */
+  static int GetStressRuns();
+
+  /**
+   * Indicate the number of the run which is about to start. The value of run
+   * should be between 0 and one less than the result from GetStressRuns()
+   */
+  static void PrepareStressRun(int run);
+
+  /**
+   * Force deoptimization of all functions.
+   */
+  static void DeoptimizeAll();
+};
+
+
+}  // namespace v8
+
+#endif  // V8_V8_TEST_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8-util.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8-util.h
new file mode 100644
index 0000000..73ec658
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8-util.h
@@ -0,0 +1,643 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_UTIL_H_
+#define V8_UTIL_H_
+
+#include "v8.h"  // NOLINT(build/include)
+#include <map>
+#include <vector>
+
+/**
+ * Support for Persistent containers.
+ *
+ * C++11 embedders can use STL containers with Global values,
+ * but pre-C++11 does not support the required move semantic and hence
+ * may want these container classes.
+ */
+namespace v8 {
+
+typedef uintptr_t PersistentContainerValue;
+static const uintptr_t kPersistentContainerNotFound = 0;
+enum PersistentContainerCallbackType {
+  kNotWeak,
+  // These correspond to v8::WeakCallbackType
+  kWeakWithParameter,
+  kWeakWithInternalFields,
+  kWeak = kWeakWithParameter  // For backwards compatibility.  Deprecate.
+};
+
+
+/**
+ * A default trait implemenation for PersistentValueMap which uses std::map
+ * as a backing map.
+ *
+ * Users will have to implement their own weak callbacks & dispose traits.
+ */
+template<typename K, typename V>
+class StdMapTraits {
+ public:
+  // STL map & related:
+  typedef std::map<K, PersistentContainerValue> Impl;
+  typedef typename Impl::iterator Iterator;
+
+  static bool Empty(Impl* impl) { return impl->empty(); }
+  static size_t Size(Impl* impl) { return impl->size(); }
+  static void Swap(Impl& a, Impl& b) { std::swap(a, b); }  // NOLINT
+  static Iterator Begin(Impl* impl) { return impl->begin(); }
+  static Iterator End(Impl* impl) { return impl->end(); }
+  static K Key(Iterator it) { return it->first; }
+  static PersistentContainerValue Value(Iterator it) { return it->second; }
+  static PersistentContainerValue Set(Impl* impl, K key,
+      PersistentContainerValue value) {
+    std::pair<Iterator, bool> res = impl->insert(std::make_pair(key, value));
+    PersistentContainerValue old_value = kPersistentContainerNotFound;
+    if (!res.second) {
+      old_value = res.first->second;
+      res.first->second = value;
+    }
+    return old_value;
+  }
+  static PersistentContainerValue Get(Impl* impl, K key) {
+    Iterator it = impl->find(key);
+    if (it == impl->end()) return kPersistentContainerNotFound;
+    return it->second;
+  }
+  static PersistentContainerValue Remove(Impl* impl, K key) {
+    Iterator it = impl->find(key);
+    if (it == impl->end()) return kPersistentContainerNotFound;
+    PersistentContainerValue value = it->second;
+    impl->erase(it);
+    return value;
+  }
+};
+
+
+/**
+ * A default trait implementation for PersistentValueMap, which inherits
+ * a std:map backing map from StdMapTraits and holds non-weak persistent
+ * objects and has no special Dispose handling.
+ *
+ * You should not derive from this class, since MapType depends on the
+ * surrounding class, and hence a subclass cannot simply inherit the methods.
+ */
+template<typename K, typename V>
+class DefaultPersistentValueMapTraits : public StdMapTraits<K, V> {
+ public:
+  // Weak callback & friends:
+  static const PersistentContainerCallbackType kCallbackType = kNotWeak;
+  typedef PersistentValueMap<K, V, DefaultPersistentValueMapTraits<K, V> >
+      MapType;
+  typedef void WeakCallbackDataType;
+
+  static WeakCallbackDataType* WeakCallbackParameter(
+      MapType* map, const K& key, Local<V> value) {
+    return NULL;
+  }
+  static MapType* MapFromWeakCallbackData(
+          const WeakCallbackData<V, WeakCallbackDataType>& data) {
+    return NULL;
+  }
+  static K KeyFromWeakCallbackData(
+      const WeakCallbackData<V, WeakCallbackDataType>& data) {
+    return K();
+  }
+  static void DisposeCallbackData(WeakCallbackDataType* data) { }
+  static void Dispose(Isolate* isolate, Global<V> value, K key) {}
+};
+
+
+template <typename K, typename V>
+class DefaultGlobalMapTraits : public StdMapTraits<K, V> {
+ private:
+  template <typename T>
+  struct RemovePointer;
+
+ public:
+  // Weak callback & friends:
+  static const PersistentContainerCallbackType kCallbackType = kNotWeak;
+  typedef GlobalValueMap<K, V, DefaultGlobalMapTraits<K, V> > MapType;
+  typedef void WeakCallbackDataType;
+
+  static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key,
+                                                     Local<V> value) {
+    return nullptr;
+  }
+  static MapType* MapFromWeakCallbackInfo(
+      const WeakCallbackInfo<WeakCallbackDataType>& data) {
+    return nullptr;
+  }
+  static K KeyFromWeakCallbackInfo(
+      const WeakCallbackInfo<WeakCallbackDataType>& data) {
+    return K();
+  }
+  static void DisposeCallbackData(WeakCallbackDataType* data) {}
+  static void OnWeakCallback(
+      const WeakCallbackInfo<WeakCallbackDataType>& data) {}
+  static void Dispose(Isolate* isolate, Global<V> value, K key) {}
+  // This is a second pass callback, so SetSecondPassCallback cannot be called.
+  static void DisposeWeak(const WeakCallbackInfo<WeakCallbackDataType>& data) {}
+
+ private:
+  template <typename T>
+  struct RemovePointer<T*> {
+    typedef T Type;
+  };
+};
+
+
+/**
+ * A map wrapper that allows using Global as a mapped value.
+ * C++11 embedders don't need this class, as they can use Global
+ * directly in std containers.
+ *
+ * The map relies on a backing map, whose type and accessors are described
+ * by the Traits class. The backing map will handle values of type
+ * PersistentContainerValue, with all conversion into and out of V8
+ * handles being transparently handled by this class.
+ */
+template <typename K, typename V, typename Traits>
+class PersistentValueMapBase {
+ public:
+  Isolate* GetIsolate() { return isolate_; }
+
+  /**
+   * Return size of the map.
+   */
+  size_t Size() { return Traits::Size(&impl_); }
+
+  /**
+   * Return whether the map holds weak persistents.
+   */
+  bool IsWeak() { return Traits::kCallbackType != kNotWeak; }
+
+  /**
+   * Get value stored in map.
+   */
+  Local<V> Get(const K& key) {
+    return Local<V>::New(isolate_, FromVal(Traits::Get(&impl_, key)));
+  }
+
+  /**
+   * Check whether a value is contained in the map.
+   */
+  bool Contains(const K& key) {
+    return Traits::Get(&impl_, key) != kPersistentContainerNotFound;
+  }
+
+  /**
+   * Get value stored in map and set it in returnValue.
+   * Return true if a value was found.
+   */
+  bool SetReturnValue(const K& key,
+      ReturnValue<Value> returnValue) {
+    return SetReturnValueFromVal(&returnValue, Traits::Get(&impl_, key));
+  }
+
+  /**
+   * Call Isolate::SetReference with the given parent and the map value.
+   */
+  void SetReference(const K& key,
+      const Persistent<Object>& parent) {
+    GetIsolate()->SetReference(
+      reinterpret_cast<internal::Object**>(parent.val_),
+      reinterpret_cast<internal::Object**>(FromVal(Traits::Get(&impl_, key))));
+  }
+
+  /**
+   * Return value for key and remove it from the map.
+   */
+  Global<V> Remove(const K& key) {
+    return Release(Traits::Remove(&impl_, key)).Pass();
+  }
+
+  /**
+  * Traverses the map repeatedly,
+  * in case side effects of disposal cause insertions.
+  **/
+  void Clear() {
+    typedef typename Traits::Iterator It;
+    HandleScope handle_scope(isolate_);
+    // TODO(dcarney): figure out if this swap and loop is necessary.
+    while (!Traits::Empty(&impl_)) {
+      typename Traits::Impl impl;
+      Traits::Swap(impl_, impl);
+      for (It i = Traits::Begin(&impl); i != Traits::End(&impl); ++i) {
+        Traits::Dispose(isolate_, Release(Traits::Value(i)).Pass(),
+                        Traits::Key(i));
+      }
+    }
+  }
+
+  /**
+   * Helper class for GetReference/SetWithReference. Do not use outside
+   * that context.
+   */
+  class PersistentValueReference {
+   public:
+    PersistentValueReference() : value_(kPersistentContainerNotFound) { }
+    PersistentValueReference(const PersistentValueReference& other)
+        : value_(other.value_) { }
+
+    Local<V> NewLocal(Isolate* isolate) const {
+      return Local<V>::New(isolate, FromVal(value_));
+    }
+    bool IsEmpty() const {
+      return value_ == kPersistentContainerNotFound;
+    }
+    template<typename T>
+    bool SetReturnValue(ReturnValue<T> returnValue) {
+      return SetReturnValueFromVal(&returnValue, value_);
+    }
+    void Reset() {
+      value_ = kPersistentContainerNotFound;
+    }
+    void operator=(const PersistentValueReference& other) {
+      value_ = other.value_;
+    }
+
+   private:
+    friend class PersistentValueMapBase;
+    friend class PersistentValueMap<K, V, Traits>;
+    friend class GlobalValueMap<K, V, Traits>;
+
+    explicit PersistentValueReference(PersistentContainerValue value)
+        : value_(value) { }
+
+    void operator=(PersistentContainerValue value) {
+      value_ = value;
+    }
+
+    PersistentContainerValue value_;
+  };
+
+  /**
+   * Get a reference to a map value. This enables fast, repeated access
+   * to a value stored in the map while the map remains unchanged.
+   *
+   * Careful: This is potentially unsafe, so please use with care.
+   * The value will become invalid if the value for this key changes
+   * in the underlying map, as a result of Set or Remove for the same
+   * key; as a result of the weak callback for the same key; or as a
+   * result of calling Clear() or destruction of the map.
+   */
+  PersistentValueReference GetReference(const K& key) {
+    return PersistentValueReference(Traits::Get(&impl_, key));
+  }
+
+ protected:
+  explicit PersistentValueMapBase(Isolate* isolate) : isolate_(isolate) {}
+
+  ~PersistentValueMapBase() { Clear(); }
+
+  Isolate* isolate() { return isolate_; }
+  typename Traits::Impl* impl() { return &impl_; }
+
+  static V* FromVal(PersistentContainerValue v) {
+    return reinterpret_cast<V*>(v);
+  }
+
+  static PersistentContainerValue ClearAndLeak(Global<V>* persistent) {
+    V* v = persistent->val_;
+    persistent->val_ = 0;
+    return reinterpret_cast<PersistentContainerValue>(v);
+  }
+
+  static PersistentContainerValue Leak(Global<V>* persistent) {
+    return reinterpret_cast<PersistentContainerValue>(persistent->val_);
+  }
+
+  /**
+   * Return a container value as Global and make sure the weak
+   * callback is properly disposed of. All remove functionality should go
+   * through this.
+   */
+  static Global<V> Release(PersistentContainerValue v) {
+    Global<V> p;
+    p.val_ = FromVal(v);
+    if (Traits::kCallbackType != kNotWeak && p.IsWeak()) {
+      Traits::DisposeCallbackData(
+          p.template ClearWeak<typename Traits::WeakCallbackDataType>());
+    }
+    return p.Pass();
+  }
+
+  void RemoveWeak(const K& key) {
+    Global<V> p;
+    p.val_ = FromVal(Traits::Remove(&impl_, key));
+    p.Reset();
+  }
+
+ private:
+  PersistentValueMapBase(PersistentValueMapBase&);
+  void operator=(PersistentValueMapBase&);
+
+  static bool SetReturnValueFromVal(ReturnValue<Value>* returnValue,
+                                    PersistentContainerValue value) {
+    bool hasValue = value != kPersistentContainerNotFound;
+    if (hasValue) {
+      returnValue->SetInternal(
+          *reinterpret_cast<internal::Object**>(FromVal(value)));
+    }
+    return hasValue;
+  }
+
+  Isolate* isolate_;
+  typename Traits::Impl impl_;
+};
+
+
+template <typename K, typename V, typename Traits>
+class PersistentValueMap : public PersistentValueMapBase<K, V, Traits> {
+ public:
+  explicit PersistentValueMap(Isolate* isolate)
+      : PersistentValueMapBase<K, V, Traits>(isolate) {}
+
+  typedef
+      typename PersistentValueMapBase<K, V, Traits>::PersistentValueReference
+          PersistentValueReference;
+
+  /**
+   * Put value into map. Depending on Traits::kIsWeak, the value will be held
+   * by the map strongly or weakly.
+   * Returns old value as Global.
+   */
+  Global<V> Set(const K& key, Local<V> value) {
+    Global<V> persistent(this->isolate(), value);
+    return SetUnique(key, &persistent);
+  }
+
+  /**
+   * Put value into map, like Set(const K&, Local<V>).
+   */
+  Global<V> Set(const K& key, Global<V> value) {
+    return SetUnique(key, &value);
+  }
+
+  /**
+   * Put the value into the map, and set the 'weak' callback when demanded
+   * by the Traits class.
+   */
+  Global<V> SetUnique(const K& key, Global<V>* persistent) {
+    if (Traits::kCallbackType != kNotWeak) {
+      Local<V> value(Local<V>::New(this->isolate(), *persistent));
+      persistent->template SetWeak<typename Traits::WeakCallbackDataType>(
+        Traits::WeakCallbackParameter(this, key, value), WeakCallback);
+    }
+    PersistentContainerValue old_value =
+        Traits::Set(this->impl(), key, this->ClearAndLeak(persistent));
+    return this->Release(old_value).Pass();
+  }
+
+  /**
+   * Put a value into the map and update the reference.
+   * Restrictions of GetReference apply here as well.
+   */
+  Global<V> Set(const K& key, Global<V> value,
+                PersistentValueReference* reference) {
+    *reference = this->Leak(&value);
+    return SetUnique(key, &value);
+  }
+
+ private:
+  static void WeakCallback(
+      const WeakCallbackData<V, typename Traits::WeakCallbackDataType>& data) {
+    if (Traits::kCallbackType != kNotWeak) {
+      PersistentValueMap<K, V, Traits>* persistentValueMap =
+          Traits::MapFromWeakCallbackData(data);
+      K key = Traits::KeyFromWeakCallbackData(data);
+      Traits::Dispose(data.GetIsolate(),
+                      persistentValueMap->Remove(key).Pass(), key);
+      Traits::DisposeCallbackData(data.GetParameter());
+    }
+  }
+};
+
+
+template <typename K, typename V, typename Traits>
+class GlobalValueMap : public PersistentValueMapBase<K, V, Traits> {
+ public:
+  explicit GlobalValueMap(Isolate* isolate)
+      : PersistentValueMapBase<K, V, Traits>(isolate) {}
+
+  typedef
+      typename PersistentValueMapBase<K, V, Traits>::PersistentValueReference
+          PersistentValueReference;
+
+  /**
+   * Put value into map. Depending on Traits::kIsWeak, the value will be held
+   * by the map strongly or weakly.
+   * Returns old value as Global.
+   */
+  Global<V> Set(const K& key, Local<V> value) {
+    Global<V> persistent(this->isolate(), value);
+    return SetUnique(key, &persistent);
+  }
+
+  /**
+   * Put value into map, like Set(const K&, Local<V>).
+   */
+  Global<V> Set(const K& key, Global<V> value) {
+    return SetUnique(key, &value);
+  }
+
+  /**
+   * Put the value into the map, and set the 'weak' callback when demanded
+   * by the Traits class.
+   */
+  Global<V> SetUnique(const K& key, Global<V>* persistent) {
+    if (Traits::kCallbackType != kNotWeak) {
+      WeakCallbackType callback_type =
+          Traits::kCallbackType == kWeakWithInternalFields
+              ? WeakCallbackType::kInternalFields
+              : WeakCallbackType::kParameter;
+      Local<V> value(Local<V>::New(this->isolate(), *persistent));
+      persistent->template SetWeak<typename Traits::WeakCallbackDataType>(
+          Traits::WeakCallbackParameter(this, key, value), OnWeakCallback,
+          callback_type);
+    }
+    PersistentContainerValue old_value =
+        Traits::Set(this->impl(), key, this->ClearAndLeak(persistent));
+    return this->Release(old_value).Pass();
+  }
+
+  /**
+   * Put a value into the map and update the reference.
+   * Restrictions of GetReference apply here as well.
+   */
+  Global<V> Set(const K& key, Global<V> value,
+                PersistentValueReference* reference) {
+    *reference = this->Leak(&value);
+    return SetUnique(key, &value);
+  }
+
+ private:
+  static void OnWeakCallback(
+      const WeakCallbackInfo<typename Traits::WeakCallbackDataType>& data) {
+    if (Traits::kCallbackType != kNotWeak) {
+      auto map = Traits::MapFromWeakCallbackInfo(data);
+      K key = Traits::KeyFromWeakCallbackInfo(data);
+      map->RemoveWeak(key);
+      Traits::OnWeakCallback(data);
+      data.SetSecondPassCallback(SecondWeakCallback);
+    }
+  }
+
+  static void SecondWeakCallback(
+      const WeakCallbackInfo<typename Traits::WeakCallbackDataType>& data) {
+    Traits::DisposeWeak(data);
+  }
+};
+
+
+/**
+ * A map that uses Global as value and std::map as the backing
+ * implementation. Persistents are held non-weak.
+ *
+ * C++11 embedders don't need this class, as they can use
+ * Global directly in std containers.
+ */
+template<typename K, typename V,
+    typename Traits = DefaultPersistentValueMapTraits<K, V> >
+class StdPersistentValueMap : public PersistentValueMap<K, V, Traits> {
+ public:
+  explicit StdPersistentValueMap(Isolate* isolate)
+      : PersistentValueMap<K, V, Traits>(isolate) {}
+};
+
+
+/**
+ * A map that uses Global as value and std::map as the backing
+ * implementation. Globals are held non-weak.
+ *
+ * C++11 embedders don't need this class, as they can use
+ * Global directly in std containers.
+ */
+template <typename K, typename V,
+          typename Traits = DefaultGlobalMapTraits<K, V> >
+class StdGlobalValueMap : public GlobalValueMap<K, V, Traits> {
+ public:
+  explicit StdGlobalValueMap(Isolate* isolate)
+      : GlobalValueMap<K, V, Traits>(isolate) {}
+};
+
+
+class DefaultPersistentValueVectorTraits {
+ public:
+  typedef std::vector<PersistentContainerValue> Impl;
+
+  static void Append(Impl* impl, PersistentContainerValue value) {
+    impl->push_back(value);
+  }
+  static bool IsEmpty(const Impl* impl) {
+    return impl->empty();
+  }
+  static size_t Size(const Impl* impl) {
+    return impl->size();
+  }
+  static PersistentContainerValue Get(const Impl* impl, size_t i) {
+    return (i < impl->size()) ? impl->at(i) : kPersistentContainerNotFound;
+  }
+  static void ReserveCapacity(Impl* impl, size_t capacity) {
+    impl->reserve(capacity);
+  }
+  static void Clear(Impl* impl) {
+    impl->clear();
+  }
+};
+
+
+/**
+ * A vector wrapper that safely stores Global values.
+ * C++11 embedders don't need this class, as they can use Global
+ * directly in std containers.
+ *
+ * This class relies on a backing vector implementation, whose type and methods
+ * are described by the Traits class. The backing map will handle values of type
+ * PersistentContainerValue, with all conversion into and out of V8
+ * handles being transparently handled by this class.
+ */
+template<typename V, typename Traits = DefaultPersistentValueVectorTraits>
+class PersistentValueVector {
+ public:
+  explicit PersistentValueVector(Isolate* isolate) : isolate_(isolate) { }
+
+  ~PersistentValueVector() {
+    Clear();
+  }
+
+  /**
+   * Append a value to the vector.
+   */
+  void Append(Local<V> value) {
+    Global<V> persistent(isolate_, value);
+    Traits::Append(&impl_, ClearAndLeak(&persistent));
+  }
+
+  /**
+   * Append a persistent's value to the vector.
+   */
+  void Append(Global<V> persistent) {
+    Traits::Append(&impl_, ClearAndLeak(&persistent));
+  }
+
+  /**
+   * Are there any values in the vector?
+   */
+  bool IsEmpty() const {
+    return Traits::IsEmpty(&impl_);
+  }
+
+  /**
+   * How many elements are in the vector?
+   */
+  size_t Size() const {
+    return Traits::Size(&impl_);
+  }
+
+  /**
+   * Retrieve the i-th value in the vector.
+   */
+  Local<V> Get(size_t index) const {
+    return Local<V>::New(isolate_, FromVal(Traits::Get(&impl_, index)));
+  }
+
+  /**
+   * Remove all elements from the vector.
+   */
+  void Clear() {
+    size_t length = Traits::Size(&impl_);
+    for (size_t i = 0; i < length; i++) {
+      Global<V> p;
+      p.val_ = FromVal(Traits::Get(&impl_, i));
+    }
+    Traits::Clear(&impl_);
+  }
+
+  /**
+   * Reserve capacity in the vector.
+   * (Efficiency gains depend on the backing implementation.)
+   */
+  void ReserveCapacity(size_t capacity) {
+    Traits::ReserveCapacity(&impl_, capacity);
+  }
+
+ private:
+  static PersistentContainerValue ClearAndLeak(Global<V>* persistent) {
+    V* v = persistent->val_;
+    persistent->val_ = 0;
+    return reinterpret_cast<PersistentContainerValue>(v);
+  }
+
+  static V* FromVal(PersistentContainerValue v) {
+    return reinterpret_cast<V*>(v);
+  }
+
+  Isolate* isolate_;
+  typename Traits::Impl impl_;
+};
+
+}  // namespace v8
+
+#endif  // V8_UTIL_H
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8-version.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8-version.h
new file mode 100644
index 0000000..95dae8d
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8-version.h
@@ -0,0 +1,20 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_INCLUDE_VERSION_H_  // V8_VERSION_H_ conflicts with src/version.h
+#define V8_INCLUDE_VERSION_H_
+
+// These macros define the version number for the current version.
+// NOTE these macros are used by some of the tool scripts and the build
+// system so their names cannot be changed without changing the scripts.
+#define V8_MAJOR_VERSION 4
+#define V8_MINOR_VERSION 8
+#define V8_BUILD_NUMBER 175
+#define V8_PATCH_LEVEL 0
+
+// Use 1 for candidates and 0 otherwise.
+// (Boolean macro values are not supported by all preprocessors.)
+#define V8_IS_CANDIDATE_VERSION 0
+
+#endif  // V8_INCLUDE_VERSION_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8.h
new file mode 100644
index 0000000..96a777b
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8.h
@@ -0,0 +1,8442 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/** \mainpage V8 API Reference Guide
+ *
+ * V8 is Google's open source JavaScript engine.
+ *
+ * This set of documents provides reference material generated from the
+ * V8 header file, include/v8.h.
+ *
+ * For other documentation see http://code.google.com/apis/v8/
+ */
+
+#ifndef V8_H_
+#define V8_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "v8-version.h"  // NOLINT(build/include)
+#include "v8config.h"    // NOLINT(build/include)
+
+// We reserve the V8_* prefix for macros defined in V8 public API and
+// assume there are no name conflicts with the embedder's code.
+
+#ifdef V8_OS_WIN
+
+// Setup for Windows DLL export/import. When building the V8 DLL the
+// BUILDING_V8_SHARED needs to be defined. When building a program which uses
+// the V8 DLL USING_V8_SHARED needs to be defined. When either building the V8
+// static library or building a program which uses the V8 static library neither
+// BUILDING_V8_SHARED nor USING_V8_SHARED should be defined.
+#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
+#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
+  build configuration to ensure that at most one of these is set
+#endif
+
+#ifdef BUILDING_V8_SHARED
+# define V8_EXPORT __declspec(dllexport)
+#elif USING_V8_SHARED
+# define V8_EXPORT __declspec(dllimport)
+#else
+# define V8_EXPORT
+#endif  // BUILDING_V8_SHARED
+
+#else  // V8_OS_WIN
+
+// Setup for Linux shared library export.
+#if V8_HAS_ATTRIBUTE_VISIBILITY && defined(V8_SHARED)
+# ifdef BUILDING_V8_SHARED
+#  define V8_EXPORT __attribute__ ((visibility("default")))
+# else
+#  define V8_EXPORT
+# endif
+#else
+# define V8_EXPORT
+#endif
+
+#endif  // V8_OS_WIN
+
+/**
+ * The v8 JavaScript engine.
+ */
+namespace v8 {
+
+class AccessorSignature;
+class Array;
+class Boolean;
+class BooleanObject;
+class Context;
+class CpuProfiler;
+class Data;
+class Date;
+class External;
+class Function;
+class FunctionTemplate;
+class HeapProfiler;
+class ImplementationUtilities;
+class Int32;
+class Integer;
+class Isolate;
+template <class T>
+class Maybe;
+class Name;
+class Number;
+class NumberObject;
+class Object;
+class ObjectOperationDescriptor;
+class ObjectTemplate;
+class Platform;
+class Primitive;
+class Promise;
+class RawOperationDescriptor;
+class Script;
+class SharedArrayBuffer;
+class Signature;
+class StartupData;
+class StackFrame;
+class StackTrace;
+class String;
+class StringObject;
+class Symbol;
+class SymbolObject;
+class Uint32;
+class Utils;
+class Value;
+template <class T> class Local;
+template <class T>
+class MaybeLocal;
+template <class T> class Eternal;
+template<class T> class NonCopyablePersistentTraits;
+template<class T> class PersistentBase;
+template <class T, class M = NonCopyablePersistentTraits<T> >
+class Persistent;
+template <class T>
+class Global;
+template<class K, class V, class T> class PersistentValueMap;
+template <class K, class V, class T>
+class PersistentValueMapBase;
+template <class K, class V, class T>
+class GlobalValueMap;
+template<class V, class T> class PersistentValueVector;
+template<class T, class P> class WeakCallbackObject;
+class FunctionTemplate;
+class ObjectTemplate;
+class Data;
+template<typename T> class FunctionCallbackInfo;
+template<typename T> class PropertyCallbackInfo;
+class StackTrace;
+class StackFrame;
+class Isolate;
+class CallHandlerHelper;
+class EscapableHandleScope;
+template<typename T> class ReturnValue;
+
+namespace internal {
+class Arguments;
+class Heap;
+class HeapObject;
+class Isolate;
+class Object;
+struct StreamedSource;
+template<typename T> class CustomArguments;
+class PropertyCallbackArguments;
+class FunctionCallbackArguments;
+class GlobalHandles;
+}  // namespace internal
+
+
+/**
+ * General purpose unique identifier.
+ */
+class UniqueId {
+ public:
+  explicit UniqueId(intptr_t data)
+      : data_(data) {}
+
+  bool operator==(const UniqueId& other) const {
+    return data_ == other.data_;
+  }
+
+  bool operator!=(const UniqueId& other) const {
+    return data_ != other.data_;
+  }
+
+  bool operator<(const UniqueId& other) const {
+    return data_ < other.data_;
+  }
+
+ private:
+  intptr_t data_;
+};
+
+// --- Handles ---
+
+#define TYPE_CHECK(T, S)                                       \
+  while (false) {                                              \
+    *(static_cast<T* volatile*>(0)) = static_cast<S*>(0);      \
+  }
+
+
+/**
+ * An object reference managed by the v8 garbage collector.
+ *
+ * All objects returned from v8 have to be tracked by the garbage
+ * collector so that it knows that the objects are still alive.  Also,
+ * because the garbage collector may move objects, it is unsafe to
+ * point directly to an object.  Instead, all objects are stored in
+ * handles which are known by the garbage collector and updated
+ * whenever an object moves.  Handles should always be passed by value
+ * (except in cases like out-parameters) and they should never be
+ * allocated on the heap.
+ *
+ * There are two types of handles: local and persistent handles.
+ * Local handles are light-weight and transient and typically used in
+ * local operations.  They are managed by HandleScopes.  Persistent
+ * handles can be used when storing objects across several independent
+ * operations and have to be explicitly deallocated when they're no
+ * longer used.
+ *
+ * It is safe to extract the object stored in the handle by
+ * dereferencing the handle (for instance, to extract the Object* from
+ * a Local<Object>); the value will still be governed by a handle
+ * behind the scenes and the same rules apply to these values as to
+ * their handles.
+ */
+template <class T>
+class Local {
+ public:
+  V8_INLINE Local() : val_(0) {}
+  template <class S>
+  V8_INLINE Local(Local<S> that)
+      : val_(reinterpret_cast<T*>(*that)) {
+    /**
+     * This check fails when trying to convert between incompatible
+     * handles. For example, converting from a Local<String> to a
+     * Local<Number>.
+     */
+    TYPE_CHECK(T, S);
+  }
+
+  /**
+   * Returns true if the handle is empty.
+   */
+  V8_INLINE bool IsEmpty() const { return val_ == 0; }
+
+  /**
+   * Sets the handle to be empty. IsEmpty() will then return true.
+   */
+  V8_INLINE void Clear() { val_ = 0; }
+
+  V8_INLINE T* operator->() const { return val_; }
+
+  V8_INLINE T* operator*() const { return val_; }
+
+  /**
+   * Checks whether two handles are the same.
+   * Returns true if both are empty, or if the objects
+   * to which they refer are identical.
+   * The handles' references are not checked.
+   */
+  template <class S>
+  V8_INLINE bool operator==(const Local<S>& that) const {
+    internal::Object** a = reinterpret_cast<internal::Object**>(this->val_);
+    internal::Object** b = reinterpret_cast<internal::Object**>(that.val_);
+    if (a == 0) return b == 0;
+    if (b == 0) return false;
+    return *a == *b;
+  }
+
+  template <class S> V8_INLINE bool operator==(
+      const PersistentBase<S>& that) const {
+    internal::Object** a = reinterpret_cast<internal::Object**>(this->val_);
+    internal::Object** b = reinterpret_cast<internal::Object**>(that.val_);
+    if (a == 0) return b == 0;
+    if (b == 0) return false;
+    return *a == *b;
+  }
+
+  /**
+   * Checks whether two handles are different.
+   * Returns true if only one of the handles is empty, or if
+   * the objects to which they refer are different.
+   * The handles' references are not checked.
+   */
+  template <class S>
+  V8_INLINE bool operator!=(const Local<S>& that) const {
+    return !operator==(that);
+  }
+
+  template <class S> V8_INLINE bool operator!=(
+      const Persistent<S>& that) const {
+    return !operator==(that);
+  }
+
+  template <class S> V8_INLINE static Local<T> Cast(Local<S> that) {
+#ifdef V8_ENABLE_CHECKS
+    // If we're going to perform the type check then we have to check
+    // that the handle isn't empty before doing the checked cast.
+    if (that.IsEmpty()) return Local<T>();
+#endif
+    return Local<T>(T::Cast(*that));
+  }
+
+
+  template <class S> V8_INLINE Local<S> As() {
+    return Local<S>::Cast(*this);
+  }
+
+  /**
+   * Create a local handle for the content of another handle.
+   * The referee is kept alive by the local handle even when
+   * the original handle is destroyed/disposed.
+   */
+  V8_INLINE static Local<T> New(Isolate* isolate, Local<T> that);
+  V8_INLINE static Local<T> New(Isolate* isolate,
+                                const PersistentBase<T>& that);
+
+ private:
+  friend class Utils;
+  template<class F> friend class Eternal;
+  template<class F> friend class PersistentBase;
+  template<class F, class M> friend class Persistent;
+  template<class F> friend class Local;
+  template <class F>
+  friend class MaybeLocal;
+  template<class F> friend class FunctionCallbackInfo;
+  template<class F> friend class PropertyCallbackInfo;
+  friend class String;
+  friend class Object;
+  friend class Context;
+  template<class F> friend class internal::CustomArguments;
+  friend Local<Primitive> Undefined(Isolate* isolate);
+  friend Local<Primitive> Null(Isolate* isolate);
+  friend Local<Boolean> True(Isolate* isolate);
+  friend Local<Boolean> False(Isolate* isolate);
+  friend class HandleScope;
+  friend class EscapableHandleScope;
+  template <class F1, class F2, class F3>
+  friend class PersistentValueMapBase;
+  template<class F1, class F2> friend class PersistentValueVector;
+
+  template <class S>
+  V8_INLINE Local(S* that)
+      : val_(that) {}
+  V8_INLINE static Local<T> New(Isolate* isolate, T* that);
+  T* val_;
+};
+
+
+#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS)
+// Local is an alias for Local for historical reasons.
+template <class T>
+using Handle = Local<T>;
+#endif
+
+
+/**
+ * A MaybeLocal<> is a wrapper around Local<> that enforces a check whether
+ * the Local<> is empty before it can be used.
+ *
+ * If an API method returns a MaybeLocal<>, the API method can potentially fail
+ * either because an exception is thrown, or because an exception is pending,
+ * e.g. because a previous API call threw an exception that hasn't been caught
+ * yet, or because a TerminateExecution exception was thrown. In that case, an
+ * empty MaybeLocal is returned.
+ */
+template <class T>
+class MaybeLocal {
+ public:
+  V8_INLINE MaybeLocal() : val_(nullptr) {}
+  template <class S>
+  V8_INLINE MaybeLocal(Local<S> that)
+      : val_(reinterpret_cast<T*>(*that)) {
+    TYPE_CHECK(T, S);
+  }
+
+  V8_INLINE bool IsEmpty() const { return val_ == nullptr; }
+
+  template <class S>
+  V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local<S>* out) const {
+    out->val_ = IsEmpty() ? nullptr : this->val_;
+    return !IsEmpty();
+  }
+
+  // Will crash if the MaybeLocal<> is empty.
+  V8_INLINE Local<T> ToLocalChecked();
+
+  template <class S>
+  V8_INLINE Local<S> FromMaybe(Local<S> default_value) const {
+    return IsEmpty() ? default_value : Local<S>(val_);
+  }
+
+ private:
+  T* val_;
+};
+
+
+// Eternal handles are set-once handles that live for the life of the isolate.
+template <class T> class Eternal {
+ public:
+  V8_INLINE Eternal() : index_(kInitialValue) { }
+  template<class S>
+  V8_INLINE Eternal(Isolate* isolate, Local<S> handle) : index_(kInitialValue) {
+    Set(isolate, handle);
+  }
+  // Can only be safely called if already set.
+  V8_INLINE Local<T> Get(Isolate* isolate);
+  V8_INLINE bool IsEmpty() { return index_ == kInitialValue; }
+  template<class S> V8_INLINE void Set(Isolate* isolate, Local<S> handle);
+
+ private:
+  static const int kInitialValue = -1;
+  int index_;
+};
+
+
+static const int kInternalFieldsInWeakCallback = 2;
+
+
+template <typename T>
+class WeakCallbackInfo {
+ public:
+  typedef void (*Callback)(const WeakCallbackInfo<T>& data);
+
+  WeakCallbackInfo(Isolate* isolate, T* parameter,
+                   void* internal_fields[kInternalFieldsInWeakCallback],
+                   Callback* callback)
+      : isolate_(isolate), parameter_(parameter), callback_(callback) {
+    for (int i = 0; i < kInternalFieldsInWeakCallback; ++i) {
+      internal_fields_[i] = internal_fields[i];
+    }
+  }
+
+  V8_INLINE Isolate* GetIsolate() const { return isolate_; }
+  V8_INLINE T* GetParameter() const { return parameter_; }
+  V8_INLINE void* GetInternalField(int index) const;
+
+  V8_INLINE V8_DEPRECATE_SOON("use indexed version",
+                              void* GetInternalField1() const) {
+    return internal_fields_[0];
+  }
+  V8_INLINE V8_DEPRECATE_SOON("use indexed version",
+                              void* GetInternalField2() const) {
+    return internal_fields_[1];
+  }
+
+  bool IsFirstPass() const { return callback_ != nullptr; }
+
+  // When first called, the embedder MUST Reset() the Global which triggered the
+  // callback. The Global itself is unusable for anything else. No v8 other api
+  // calls may be called in the first callback. Should additional work be
+  // required, the embedder must set a second pass callback, which will be
+  // called after all the initial callbacks are processed.
+  // Calling SetSecondPassCallback on the second pass will immediately crash.
+  void SetSecondPassCallback(Callback callback) const { *callback_ = callback; }
+
+ private:
+  Isolate* isolate_;
+  T* parameter_;
+  Callback* callback_;
+  void* internal_fields_[kInternalFieldsInWeakCallback];
+};
+
+
+template <class T, class P>
+class WeakCallbackData {
+ public:
+  typedef void (*Callback)(const WeakCallbackData<T, P>& data);
+
+  WeakCallbackData(Isolate* isolate, P* parameter, Local<T> handle)
+      : isolate_(isolate), parameter_(parameter), handle_(handle) {}
+
+  V8_INLINE Isolate* GetIsolate() const { return isolate_; }
+  V8_INLINE P* GetParameter() const { return parameter_; }
+  V8_INLINE Local<T> GetValue() const { return handle_; }
+
+ private:
+  Isolate* isolate_;
+  P* parameter_;
+  Local<T> handle_;
+};
+
+
+// TODO(dcarney): delete this with WeakCallbackData
+template <class T>
+using PhantomCallbackData = WeakCallbackInfo<T>;
+
+
+enum class WeakCallbackType { kParameter, kInternalFields };
+
+
+/**
+ * An object reference that is independent of any handle scope.  Where
+ * a Local handle only lives as long as the HandleScope in which it was
+ * allocated, a PersistentBase handle remains valid until it is explicitly
+ * disposed.
+ *
+ * A persistent handle contains a reference to a storage cell within
+ * the v8 engine which holds an object value and which is updated by
+ * the garbage collector whenever the object is moved.  A new storage
+ * cell can be created using the constructor or PersistentBase::Reset and
+ * existing handles can be disposed using PersistentBase::Reset.
+ *
+ */
+template <class T> class PersistentBase {
+ public:
+  /**
+   * If non-empty, destroy the underlying storage cell
+   * IsEmpty() will return true after this call.
+   */
+  V8_INLINE void Reset();
+  /**
+   * If non-empty, destroy the underlying storage cell
+   * and create a new one with the contents of other if other is non empty
+   */
+  template <class S>
+  V8_INLINE void Reset(Isolate* isolate, const Local<S>& other);
+
+  /**
+   * If non-empty, destroy the underlying storage cell
+   * and create a new one with the contents of other if other is non empty
+   */
+  template <class S>
+  V8_INLINE void Reset(Isolate* isolate, const PersistentBase<S>& other);
+
+  V8_INLINE bool IsEmpty() const { return val_ == NULL; }
+  V8_INLINE void Empty() { val_ = 0; }
+
+  V8_INLINE Local<T> Get(Isolate* isolate) const {
+    return Local<T>::New(isolate, *this);
+  }
+
+  template <class S>
+  V8_INLINE bool operator==(const PersistentBase<S>& that) const {
+    internal::Object** a = reinterpret_cast<internal::Object**>(this->val_);
+    internal::Object** b = reinterpret_cast<internal::Object**>(that.val_);
+    if (a == NULL) return b == NULL;
+    if (b == NULL) return false;
+    return *a == *b;
+  }
+
+  template <class S>
+  V8_INLINE bool operator==(const Local<S>& that) const {
+    internal::Object** a = reinterpret_cast<internal::Object**>(this->val_);
+    internal::Object** b = reinterpret_cast<internal::Object**>(that.val_);
+    if (a == NULL) return b == NULL;
+    if (b == NULL) return false;
+    return *a == *b;
+  }
+
+  template <class S>
+  V8_INLINE bool operator!=(const PersistentBase<S>& that) const {
+    return !operator==(that);
+  }
+
+  template <class S>
+  V8_INLINE bool operator!=(const Local<S>& that) const {
+    return !operator==(that);
+  }
+
+  /**
+   *  Install a finalization callback on this object.
+   *  NOTE: There is no guarantee as to *when* or even *if* the callback is
+   *  invoked. The invocation is performed solely on a best effort basis.
+   *  As always, GC-based finalization should *not* be relied upon for any
+   *  critical form of resource management!
+   */
+  template <typename P>
+  V8_INLINE V8_DEPRECATE_SOON(
+      "use WeakCallbackInfo version",
+      void SetWeak(P* parameter,
+                   typename WeakCallbackData<T, P>::Callback callback));
+
+  template <typename S, typename P>
+  V8_INLINE V8_DEPRECATE_SOON(
+      "use WeakCallbackInfo version",
+      void SetWeak(P* parameter,
+                   typename WeakCallbackData<S, P>::Callback callback));
+
+  // Phantom persistents work like weak persistents, except that the pointer to
+  // the object being collected is not available in the finalization callback.
+  // This enables the garbage collector to collect the object and any objects
+  // it references transitively in one GC cycle. At the moment you can either
+  // specify a parameter for the callback or the location of two internal
+  // fields in the dying object.
+  template <typename P>
+  V8_INLINE V8_DEPRECATE_SOON(
+      "use SetWeak",
+      void SetPhantom(P* parameter,
+                      typename WeakCallbackInfo<P>::Callback callback,
+                      int internal_field_index1 = -1,
+                      int internal_field_index2 = -1));
+
+  template <typename P>
+  V8_INLINE void SetWeak(P* parameter,
+                         typename WeakCallbackInfo<P>::Callback callback,
+                         WeakCallbackType type);
+
+  template<typename P>
+  V8_INLINE P* ClearWeak();
+
+  // TODO(dcarney): remove this.
+  V8_INLINE void ClearWeak() { ClearWeak<void>(); }
+
+  /**
+   * Marks the reference to this object independent. Garbage collector is free
+   * to ignore any object groups containing this object. Weak callback for an
+   * independent handle should not assume that it will be preceded by a global
+   * GC prologue callback or followed by a global GC epilogue callback.
+   */
+  V8_INLINE void MarkIndependent();
+
+  /**
+   * Marks the reference to this object partially dependent. Partially dependent
+   * handles only depend on other partially dependent handles and these
+   * dependencies are provided through object groups. It provides a way to build
+   * smaller object groups for young objects that represent only a subset of all
+   * external dependencies. This mark is automatically cleared after each
+   * garbage collection.
+   */
+  V8_INLINE void MarkPartiallyDependent();
+
+  /**
+   * Marks the reference to this object as active. The scavenge garbage
+   * collection should not reclaim the objects marked as active.
+   * This bit is cleared after the each garbage collection pass.
+   */
+  V8_INLINE void MarkActive();
+
+  V8_INLINE bool IsIndependent() const;
+
+  /** Checks if the handle holds the only reference to an object. */
+  V8_INLINE bool IsNearDeath() const;
+
+  /** Returns true if the handle's reference is weak.  */
+  V8_INLINE bool IsWeak() const;
+
+  /**
+   * Assigns a wrapper class ID to the handle. See RetainedObjectInfo interface
+   * description in v8-profiler.h for details.
+   */
+  V8_INLINE void SetWrapperClassId(uint16_t class_id);
+
+  /**
+   * Returns the class ID previously assigned to this handle or 0 if no class ID
+   * was previously assigned.
+   */
+  V8_INLINE uint16_t WrapperClassId() const;
+
+ private:
+  friend class Isolate;
+  friend class Utils;
+  template<class F> friend class Local;
+  template<class F1, class F2> friend class Persistent;
+  template <class F>
+  friend class Global;
+  template<class F> friend class PersistentBase;
+  template<class F> friend class ReturnValue;
+  template <class F1, class F2, class F3>
+  friend class PersistentValueMapBase;
+  template<class F1, class F2> friend class PersistentValueVector;
+  friend class Object;
+
+  explicit V8_INLINE PersistentBase(T* val) : val_(val) {}
+  PersistentBase(const PersistentBase& other) = delete;  // NOLINT
+  void operator=(const PersistentBase&) = delete;
+  V8_INLINE static T* New(Isolate* isolate, T* that);
+
+  T* val_;
+};
+
+
+/**
+ * Default traits for Persistent. This class does not allow
+ * use of the copy constructor or assignment operator.
+ * At present kResetInDestructor is not set, but that will change in a future
+ * version.
+ */
+template<class T>
+class NonCopyablePersistentTraits {
+ public:
+  typedef Persistent<T, NonCopyablePersistentTraits<T> > NonCopyablePersistent;
+  static const bool kResetInDestructor = false;
+  template<class S, class M>
+  V8_INLINE static void Copy(const Persistent<S, M>& source,
+                             NonCopyablePersistent* dest) {
+    Uncompilable<Object>();
+  }
+  // TODO(dcarney): come up with a good compile error here.
+  template<class O> V8_INLINE static void Uncompilable() {
+    TYPE_CHECK(O, Primitive);
+  }
+};
+
+
+/**
+ * Helper class traits to allow copying and assignment of Persistent.
+ * This will clone the contents of storage cell, but not any of the flags, etc.
+ */
+template<class T>
+struct CopyablePersistentTraits {
+  typedef Persistent<T, CopyablePersistentTraits<T> > CopyablePersistent;
+  static const bool kResetInDestructor = true;
+  template<class S, class M>
+  static V8_INLINE void Copy(const Persistent<S, M>& source,
+                             CopyablePersistent* dest) {
+    // do nothing, just allow copy
+  }
+};
+
+
+/**
+ * A PersistentBase which allows copy and assignment.
+ *
+ * Copy, assignment and destructor bevavior is controlled by the traits
+ * class M.
+ *
+ * Note: Persistent class hierarchy is subject to future changes.
+ */
+template <class T, class M> class Persistent : public PersistentBase<T> {
+ public:
+  /**
+   * A Persistent with no storage cell.
+   */
+  V8_INLINE Persistent() : PersistentBase<T>(0) { }
+  /**
+   * Construct a Persistent from a Local.
+   * When the Local is non-empty, a new storage cell is created
+   * pointing to the same object, and no flags are set.
+   */
+  template <class S>
+  V8_INLINE Persistent(Isolate* isolate, Local<S> that)
+      : PersistentBase<T>(PersistentBase<T>::New(isolate, *that)) {
+    TYPE_CHECK(T, S);
+  }
+  /**
+   * Construct a Persistent from a Persistent.
+   * When the Persistent is non-empty, a new storage cell is created
+   * pointing to the same object, and no flags are set.
+   */
+  template <class S, class M2>
+  V8_INLINE Persistent(Isolate* isolate, const Persistent<S, M2>& that)
+    : PersistentBase<T>(PersistentBase<T>::New(isolate, *that)) {
+    TYPE_CHECK(T, S);
+  }
+  /**
+   * The copy constructors and assignment operator create a Persistent
+   * exactly as the Persistent constructor, but the Copy function from the
+   * traits class is called, allowing the setting of flags based on the
+   * copied Persistent.
+   */
+  V8_INLINE Persistent(const Persistent& that) : PersistentBase<T>(0) {
+    Copy(that);
+  }
+  template <class S, class M2>
+  V8_INLINE Persistent(const Persistent<S, M2>& that) : PersistentBase<T>(0) {
+    Copy(that);
+  }
+  V8_INLINE Persistent& operator=(const Persistent& that) { // NOLINT
+    Copy(that);
+    return *this;
+  }
+  template <class S, class M2>
+  V8_INLINE Persistent& operator=(const Persistent<S, M2>& that) { // NOLINT
+    Copy(that);
+    return *this;
+  }
+  /**
+   * The destructor will dispose the Persistent based on the
+   * kResetInDestructor flags in the traits class.  Since not calling dispose
+   * can result in a memory leak, it is recommended to always set this flag.
+   */
+  V8_INLINE ~Persistent() {
+    if (M::kResetInDestructor) this->Reset();
+  }
+
+  // TODO(dcarney): this is pretty useless, fix or remove
+  template <class S>
+  V8_INLINE static Persistent<T>& Cast(Persistent<S>& that) { // NOLINT
+#ifdef V8_ENABLE_CHECKS
+    // If we're going to perform the type check then we have to check
+    // that the handle isn't empty before doing the checked cast.
+    if (!that.IsEmpty()) T::Cast(*that);
+#endif
+    return reinterpret_cast<Persistent<T>&>(that);
+  }
+
+  // TODO(dcarney): this is pretty useless, fix or remove
+  template <class S> V8_INLINE Persistent<S>& As() { // NOLINT
+    return Persistent<S>::Cast(*this);
+  }
+
+ private:
+  friend class Isolate;
+  friend class Utils;
+  template<class F> friend class Local;
+  template<class F1, class F2> friend class Persistent;
+  template<class F> friend class ReturnValue;
+
+  template <class S> V8_INLINE Persistent(S* that) : PersistentBase<T>(that) { }
+  V8_INLINE T* operator*() const { return this->val_; }
+  template<class S, class M2>
+  V8_INLINE void Copy(const Persistent<S, M2>& that);
+};
+
+
+/**
+ * A PersistentBase which has move semantics.
+ *
+ * Note: Persistent class hierarchy is subject to future changes.
+ */
+template <class T>
+class Global : public PersistentBase<T> {
+ public:
+  /**
+   * A Global with no storage cell.
+   */
+  V8_INLINE Global() : PersistentBase<T>(nullptr) {}
+  /**
+   * Construct a Global from a Local.
+   * When the Local is non-empty, a new storage cell is created
+   * pointing to the same object, and no flags are set.
+   */
+  template <class S>
+  V8_INLINE Global(Isolate* isolate, Local<S> that)
+      : PersistentBase<T>(PersistentBase<T>::New(isolate, *that)) {
+    TYPE_CHECK(T, S);
+  }
+  /**
+   * Construct a Global from a PersistentBase.
+   * When the Persistent is non-empty, a new storage cell is created
+   * pointing to the same object, and no flags are set.
+   */
+  template <class S>
+  V8_INLINE Global(Isolate* isolate, const PersistentBase<S>& that)
+      : PersistentBase<T>(PersistentBase<T>::New(isolate, that.val_)) {
+    TYPE_CHECK(T, S);
+  }
+  /**
+   * Move constructor.
+   */
+  V8_INLINE Global(Global&& other) : PersistentBase<T>(other.val_) {  // NOLINT
+    other.val_ = nullptr;
+  }
+  V8_INLINE ~Global() { this->Reset(); }
+  /**
+   * Move via assignment.
+   */
+  template <class S>
+  V8_INLINE Global& operator=(Global<S>&& rhs) {  // NOLINT
+    TYPE_CHECK(T, S);
+    if (this != &rhs) {
+      this->Reset();
+      this->val_ = rhs.val_;
+      rhs.val_ = nullptr;
+    }
+    return *this;
+  }
+  /**
+   * Pass allows returning uniques from functions, etc.
+   */
+  Global Pass() { return static_cast<Global&&>(*this); }  // NOLINT
+
+  /*
+   * For compatibility with Chromium's base::Bind (base::Passed).
+   */
+  typedef void MoveOnlyTypeForCPP03;
+
+ private:
+  template <class F>
+  friend class ReturnValue;
+  Global(const Global&) = delete;
+  void operator=(const Global&) = delete;
+  V8_INLINE T* operator*() const { return this->val_; }
+};
+
+
+// UniquePersistent is an alias for Global for historical reason.
+template <class T>
+using UniquePersistent = Global<T>;
+
+
+ /**
+ * A stack-allocated class that governs a number of local handles.
+ * After a handle scope has been created, all local handles will be
+ * allocated within that handle scope until either the handle scope is
+ * deleted or another handle scope is created.  If there is already a
+ * handle scope and a new one is created, all allocations will take
+ * place in the new handle scope until it is deleted.  After that,
+ * new handles will again be allocated in the original handle scope.
+ *
+ * After the handle scope of a local handle has been deleted the
+ * garbage collector will no longer track the object stored in the
+ * handle and may deallocate it.  The behavior of accessing a handle
+ * for which the handle scope has been deleted is undefined.
+ */
+class V8_EXPORT HandleScope {
+ public:
+  HandleScope(Isolate* isolate);
+
+  ~HandleScope();
+
+  /**
+   * Counts the number of allocated handles.
+   */
+  static int NumberOfHandles(Isolate* isolate);
+
+  V8_INLINE Isolate* GetIsolate() const {
+    return reinterpret_cast<Isolate*>(isolate_);
+  }
+
+ protected:
+  V8_INLINE HandleScope() {}
+
+  void Initialize(Isolate* isolate);
+
+  static internal::Object** CreateHandle(internal::Isolate* isolate,
+                                         internal::Object* value);
+
+ private:
+  // Uses heap_object to obtain the current Isolate.
+  static internal::Object** CreateHandle(internal::HeapObject* heap_object,
+                                         internal::Object* value);
+
+  // Make it hard to create heap-allocated or illegal handle scopes by
+  // disallowing certain operations.
+  HandleScope(const HandleScope&);
+  void operator=(const HandleScope&);
+  //void* operator new(size_t size);
+  //void operator delete(void*, size_t);
+
+  internal::Isolate* isolate_;
+  internal::Object** prev_next_;
+  internal::Object** prev_limit_;
+
+  // Local::New uses CreateHandle with an Isolate* parameter.
+  template<class F> friend class Local;
+
+  // Object::GetInternalField and Context::GetEmbedderData use CreateHandle with
+  // a HeapObject* in their shortcuts.
+  friend class Object;
+  friend class Context;
+};
+
+
+/**
+ * A HandleScope which first allocates a handle in the current scope
+ * which will be later filled with the escape value.
+ */
+class V8_EXPORT EscapableHandleScope : public HandleScope {
+ public:
+  EscapableHandleScope(Isolate* isolate);
+  V8_INLINE ~EscapableHandleScope() {}
+
+  /**
+   * Pushes the value into the previous scope and returns a handle to it.
+   * Cannot be called twice.
+   */
+  template <class T>
+  V8_INLINE Local<T> Escape(Local<T> value) {
+    internal::Object** slot =
+        Escape(reinterpret_cast<internal::Object**>(*value));
+    return Local<T>(reinterpret_cast<T*>(slot));
+  }
+
+ private:
+  internal::Object** Escape(internal::Object** escape_value);
+
+  // Make it hard to create heap-allocated or illegal handle scopes by
+  // disallowing certain operations.
+  EscapableHandleScope(const EscapableHandleScope&);
+  void operator=(const EscapableHandleScope&);
+  void* operator new(size_t size);
+  void operator delete(void*, size_t);
+
+  internal::Object** escape_slot_;
+};
+
+class V8_EXPORT SealHandleScope {
+ public:
+  SealHandleScope(Isolate* isolate);
+  ~SealHandleScope();
+
+ private:
+  // Make it hard to create heap-allocated or illegal handle scopes by
+  // disallowing certain operations.
+  SealHandleScope(const SealHandleScope&);
+  void operator=(const SealHandleScope&);
+  void* operator new(size_t size);
+  void operator delete(void*, size_t);
+
+  internal::Isolate* isolate_;
+  internal::Object** prev_limit_;
+  int prev_sealed_level_;
+};
+
+
+// --- Special objects ---
+
+
+/**
+ * The superclass of values and API object templates.
+ */
+class V8_EXPORT Data {
+ private:
+  Data();
+};
+
+
+/**
+ * The optional attributes of ScriptOrigin.
+ */
+class ScriptOriginOptions {
+ public:
+  V8_INLINE ScriptOriginOptions(bool is_embedder_debug_script = false,
+                                bool is_shared_cross_origin = false,
+                                bool is_opaque = false)
+      : flags_((is_embedder_debug_script ? kIsEmbedderDebugScript : 0) |
+               (is_shared_cross_origin ? kIsSharedCrossOrigin : 0) |
+               (is_opaque ? kIsOpaque : 0)) {}
+  V8_INLINE ScriptOriginOptions(int flags)
+      : flags_(flags &
+               (kIsEmbedderDebugScript | kIsSharedCrossOrigin | kIsOpaque)) {}
+  bool IsEmbedderDebugScript() const {
+    return (flags_ & kIsEmbedderDebugScript) != 0;
+  }
+  bool IsSharedCrossOrigin() const {
+    return (flags_ & kIsSharedCrossOrigin) != 0;
+  }
+  bool IsOpaque() const { return (flags_ & kIsOpaque) != 0; }
+  int Flags() const { return flags_; }
+
+ private:
+  enum {
+    kIsEmbedderDebugScript = 1,
+    kIsSharedCrossOrigin = 1 << 1,
+    kIsOpaque = 1 << 2
+  };
+  const int flags_;
+};
+
+/**
+ * The origin, within a file, of a script.
+ */
+class ScriptOrigin {
+ public:
+  V8_INLINE ScriptOrigin(
+      Local<Value> resource_name,
+      Local<Integer> resource_line_offset = Local<Integer>(),
+      Local<Integer> resource_column_offset = Local<Integer>(),
+      Local<Boolean> resource_is_shared_cross_origin = Local<Boolean>(),
+      Local<Integer> script_id = Local<Integer>(),
+      Local<Boolean> resource_is_embedder_debug_script = Local<Boolean>(),
+      Local<Value> source_map_url = Local<Value>(),
+      Local<Boolean> resource_is_opaque = Local<Boolean>());
+  V8_INLINE Local<Value> ResourceName() const;
+  V8_INLINE Local<Integer> ResourceLineOffset() const;
+  V8_INLINE Local<Integer> ResourceColumnOffset() const;
+  /**
+    * Returns true for embedder's debugger scripts
+    */
+  V8_INLINE Local<Integer> ScriptID() const;
+  V8_INLINE Local<Value> SourceMapUrl() const;
+  V8_INLINE ScriptOriginOptions Options() const { return options_; }
+
+ private:
+  Local<Value> resource_name_;
+  Local<Integer> resource_line_offset_;
+  Local<Integer> resource_column_offset_;
+  ScriptOriginOptions options_;
+  Local<Integer> script_id_;
+  Local<Value> source_map_url_;
+};
+
+
+/**
+ * A compiled JavaScript script, not yet tied to a Context.
+ */
+class V8_EXPORT UnboundScript {
+ public:
+  /**
+   * Binds the script to the currently entered context.
+   */
+  Local<Script> BindToCurrentContext();
+
+  int GetId();
+  Local<Value> GetScriptName();
+
+  /**
+   * Data read from magic sourceURL comments.
+   */
+  Local<Value> GetSourceURL();
+  /**
+   * Data read from magic sourceMappingURL comments.
+   */
+  Local<Value> GetSourceMappingURL();
+
+  /**
+   * Returns zero based line number of the code_pos location in the script.
+   * -1 will be returned if no information available.
+   */
+  int GetLineNumber(int code_pos);
+
+  static const int kNoScriptId = 0;
+};
+
+
+/**
+ * A compiled JavaScript script, tied to a Context which was active when the
+ * script was compiled.
+ */
+class V8_EXPORT Script {
+ public:
+  /**
+   * A shorthand for ScriptCompiler::Compile().
+   */
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<Script> Compile(Local<String> source,
+                            ScriptOrigin* origin = nullptr));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Script> Compile(
+      Local<Context> context, Local<String> source,
+      ScriptOrigin* origin = nullptr);
+
+  static Local<Script> V8_DEPRECATE_SOON("Use maybe version",
+                                         Compile(Local<String> source,
+                                                 Local<String> file_name));
+
+  /**
+   * Runs the script returning the resulting value. It will be run in the
+   * context in which it was created (ScriptCompiler::CompileBound or
+   * UnboundScript::BindToCurrentContext()).
+   */
+  V8_DEPRECATE_SOON("Use maybe version", Local<Value> Run());
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> Run(Local<Context> context);
+
+  /**
+   * Returns the corresponding context-unbound script.
+   */
+  Local<UnboundScript> GetUnboundScript();
+};
+
+
+/**
+ * For compiling scripts.
+ */
+class V8_EXPORT ScriptCompiler {
+ public:
+  /**
+   * Compilation data that the embedder can cache and pass back to speed up
+   * future compilations. The data is produced if the CompilerOptions passed to
+   * the compilation functions in ScriptCompiler contains produce_data_to_cache
+   * = true. The data to cache can then can be retrieved from
+   * UnboundScript.
+   */
+  struct V8_EXPORT CachedData {
+    enum BufferPolicy {
+      BufferNotOwned,
+      BufferOwned
+    };
+
+    CachedData()
+        : data(NULL),
+          length(0),
+          rejected(false),
+          buffer_policy(BufferNotOwned) {}
+
+    // If buffer_policy is BufferNotOwned, the caller keeps the ownership of
+    // data and guarantees that it stays alive until the CachedData object is
+    // destroyed. If the policy is BufferOwned, the given data will be deleted
+    // (with delete[]) when the CachedData object is destroyed.
+    CachedData(const uint8_t* data, int length,
+               BufferPolicy buffer_policy = BufferNotOwned);
+    ~CachedData();
+    // TODO(marja): Async compilation; add constructors which take a callback
+    // which will be called when V8 no longer needs the data.
+    const uint8_t* data;
+    int length;
+    bool rejected;
+    BufferPolicy buffer_policy;
+
+   private:
+    // Prevent copying. Not implemented.
+    CachedData(const CachedData&);
+    CachedData& operator=(const CachedData&);
+  };
+
+  /**
+   * Source code which can be then compiled to a UnboundScript or Script.
+   */
+  class Source {
+   public:
+    // Source takes ownership of CachedData.
+    V8_INLINE Source(Local<String> source_string, const ScriptOrigin& origin,
+           CachedData* cached_data = NULL);
+    V8_INLINE Source(Local<String> source_string,
+                     CachedData* cached_data = NULL);
+    V8_INLINE ~Source();
+
+    // Ownership of the CachedData or its buffers is *not* transferred to the
+    // caller. The CachedData object is alive as long as the Source object is
+    // alive.
+    V8_INLINE const CachedData* GetCachedData() const;
+
+   private:
+    friend class ScriptCompiler;
+    // Prevent copying. Not implemented.
+    Source(const Source&);
+    Source& operator=(const Source&);
+
+    Local<String> source_string;
+
+    // Origin information
+    Local<Value> resource_name;
+    Local<Integer> resource_line_offset;
+    Local<Integer> resource_column_offset;
+    ScriptOriginOptions resource_options;
+    Local<Value> source_map_url;
+
+    // Cached data from previous compilation (if a kConsume*Cache flag is
+    // set), or hold newly generated cache data (kProduce*Cache flags) are
+    // set when calling a compile method.
+    CachedData* cached_data;
+  };
+
+  /**
+   * For streaming incomplete script data to V8. The embedder should implement a
+   * subclass of this class.
+   */
+  class V8_EXPORT ExternalSourceStream {
+   public:
+    virtual ~ExternalSourceStream() {}
+
+    /**
+     * V8 calls this to request the next chunk of data from the embedder. This
+     * function will be called on a background thread, so it's OK to block and
+     * wait for the data, if the embedder doesn't have data yet. Returns the
+     * length of the data returned. When the data ends, GetMoreData should
+     * return 0. Caller takes ownership of the data.
+     *
+     * When streaming UTF-8 data, V8 handles multi-byte characters split between
+     * two data chunks, but doesn't handle multi-byte characters split between
+     * more than two data chunks. The embedder can avoid this problem by always
+     * returning at least 2 bytes of data.
+     *
+     * If the embedder wants to cancel the streaming, they should make the next
+     * GetMoreData call return 0. V8 will interpret it as end of data (and most
+     * probably, parsing will fail). The streaming task will return as soon as
+     * V8 has parsed the data it received so far.
+     */
+    virtual size_t GetMoreData(const uint8_t** src) = 0;
+
+    /**
+     * V8 calls this method to set a 'bookmark' at the current position in
+     * the source stream, for the purpose of (maybe) later calling
+     * ResetToBookmark. If ResetToBookmark is called later, then subsequent
+     * calls to GetMoreData should return the same data as they did when
+     * SetBookmark was called earlier.
+     *
+     * The embedder may return 'false' to indicate it cannot provide this
+     * functionality.
+     */
+    virtual bool SetBookmark();
+
+    /**
+     * V8 calls this to return to a previously set bookmark.
+     */
+    virtual void ResetToBookmark();
+  };
+
+
+  /**
+   * Source code which can be streamed into V8 in pieces. It will be parsed
+   * while streaming. It can be compiled after the streaming is complete.
+   * StreamedSource must be kept alive while the streaming task is ran (see
+   * ScriptStreamingTask below).
+   */
+  class V8_EXPORT StreamedSource {
+   public:
+    enum Encoding { ONE_BYTE, TWO_BYTE, UTF8 };
+
+    StreamedSource(ExternalSourceStream* source_stream, Encoding encoding);
+    ~StreamedSource();
+
+    // Ownership of the CachedData or its buffers is *not* transferred to the
+    // caller. The CachedData object is alive as long as the StreamedSource
+    // object is alive.
+    const CachedData* GetCachedData() const;
+
+    internal::StreamedSource* impl() const { return impl_; }
+
+   private:
+    // Prevent copying. Not implemented.
+    StreamedSource(const StreamedSource&);
+    StreamedSource& operator=(const StreamedSource&);
+
+    internal::StreamedSource* impl_;
+  };
+
+  /**
+   * A streaming task which the embedder must run on a background thread to
+   * stream scripts into V8. Returned by ScriptCompiler::StartStreamingScript.
+   */
+  class ScriptStreamingTask {
+   public:
+    virtual ~ScriptStreamingTask() {}
+    virtual void Run() = 0;
+  };
+
+  enum CompileOptions {
+    kNoCompileOptions = 0,
+    kProduceParserCache,
+    kConsumeParserCache,
+    kProduceCodeCache,
+    kConsumeCodeCache
+  };
+
+  /**
+   * Compiles the specified script (context-independent).
+   * Cached data as part of the source object can be optionally produced to be
+   * consumed later to speed up compilation of identical source scripts.
+   *
+   * Note that when producing cached data, the source must point to NULL for
+   * cached data. When consuming cached data, the cached data must have been
+   * produced by the same version of V8.
+   *
+   * \param source Script source code.
+   * \return Compiled script object (context independent; for running it must be
+   *   bound to a context).
+   */
+  static V8_DEPRECATE_SOON("Use maybe version",
+                           Local<UnboundScript> CompileUnbound(
+                               Isolate* isolate, Source* source,
+                               CompileOptions options = kNoCompileOptions));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundScript(
+      Isolate* isolate, Source* source,
+      CompileOptions options = kNoCompileOptions);
+
+  /**
+   * Compiles the specified script (bound to current context).
+   *
+   * \param source Script source code.
+   * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+   *   using pre_data speeds compilation if it's done multiple times.
+   *   Owned by caller, no references are kept when this function returns.
+   * \return Compiled script object, bound to the context that was active
+   *   when this function was called. When run it will always use this
+   *   context.
+   */
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<Script> Compile(Isolate* isolate, Source* source,
+                            CompileOptions options = kNoCompileOptions));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Script> Compile(
+      Local<Context> context, Source* source,
+      CompileOptions options = kNoCompileOptions);
+
+  /**
+   * Returns a task which streams script data into V8, or NULL if the script
+   * cannot be streamed. The user is responsible for running the task on a
+   * background thread and deleting it. When ran, the task starts parsing the
+   * script, and it will request data from the StreamedSource as needed. When
+   * ScriptStreamingTask::Run exits, all data has been streamed and the script
+   * can be compiled (see Compile below).
+   *
+   * This API allows to start the streaming with as little data as possible, and
+   * the remaining data (for example, the ScriptOrigin) is passed to Compile.
+   */
+  static ScriptStreamingTask* StartStreamingScript(
+      Isolate* isolate, StreamedSource* source,
+      CompileOptions options = kNoCompileOptions);
+
+  /**
+   * Compiles a streamed script (bound to current context).
+   *
+   * This can only be called after the streaming has finished
+   * (ScriptStreamingTask has been run). V8 doesn't construct the source string
+   * during streaming, so the embedder needs to pass the full source here.
+   */
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<Script> Compile(Isolate* isolate, StreamedSource* source,
+                            Local<String> full_source_string,
+                            const ScriptOrigin& origin));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Script> Compile(
+      Local<Context> context, StreamedSource* source,
+      Local<String> full_source_string, const ScriptOrigin& origin);
+
+  /**
+   * Return a version tag for CachedData for the current V8 version & flags.
+   *
+   * This value is meant only for determining whether a previously generated
+   * CachedData instance is still valid; the tag has no other meaing.
+   *
+   * Background: The data carried by CachedData may depend on the exact
+   *   V8 version number or currently compiler flags. This means when
+   *   persisting CachedData, the embedder must take care to not pass in
+   *   data from another V8 version, or the same version with different
+   *   features enabled.
+   *
+   *   The easiest way to do so is to clear the embedder's cache on any
+   *   such change.
+   *
+   *   Alternatively, this tag can be stored alongside the cached data and
+   *   compared when it is being used.
+   */
+  static uint32_t CachedDataVersionTag();
+
+  /**
+   * Compile an ES6 module.
+   *
+   * This is an unfinished experimental feature, and is only exposed
+   * here for internal testing purposes.
+   * Only parsing works at the moment. Do not use.
+   *
+   * TODO(adamk): Script is likely the wrong return value for this;
+   * should return some new Module type.
+   */
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Script> CompileModule(
+      Local<Context> context, Source* source,
+      CompileOptions options = kNoCompileOptions);
+
+  /**
+   * Compile a function for a given context. This is equivalent to running
+   *
+   * with (obj) {
+   *   return function(args) { ... }
+   * }
+   *
+   * It is possible to specify multiple context extensions (obj in the above
+   * example).
+   */
+  static V8_DEPRECATE_SOON("Use maybe version",
+                           Local<Function> CompileFunctionInContext(
+                               Isolate* isolate, Source* source,
+                               Local<Context> context, size_t arguments_count,
+                               Local<String> arguments[],
+                               size_t context_extension_count,
+                               Local<Object> context_extensions[]));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Function> CompileFunctionInContext(
+      Local<Context> context, Source* source, size_t arguments_count,
+      Local<String> arguments[], size_t context_extension_count,
+      Local<Object> context_extensions[]);
+
+ private:
+  static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundInternal(
+      Isolate* isolate, Source* source, CompileOptions options, bool is_module);
+};
+
+
+/**
+ * An error message.
+ */
+class V8_EXPORT Message {
+ public:
+  Local<String> Get() const;
+
+  V8_DEPRECATE_SOON("Use maybe version", Local<String> GetSourceLine() const);
+  V8_WARN_UNUSED_RESULT MaybeLocal<String> GetSourceLine(
+      Local<Context> context) const;
+
+  /**
+   * Returns the origin for the script from where the function causing the
+   * error originates.
+   */
+  ScriptOrigin GetScriptOrigin() const;
+
+  /**
+   * Returns the resource name for the script from where the function causing
+   * the error originates.
+   */
+  Local<Value> GetScriptResourceName() const;
+
+  /**
+   * Exception stack trace. By default stack traces are not captured for
+   * uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows
+   * to change this option.
+   */
+  Local<StackTrace> GetStackTrace() const;
+
+  /**
+   * Returns the number, 1-based, of the line where the error occurred.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", int GetLineNumber() const);
+  V8_WARN_UNUSED_RESULT Maybe<int> GetLineNumber(Local<Context> context) const;
+
+  /**
+   * Returns the index within the script of the first character where
+   * the error occurred.
+   */
+  int GetStartPosition() const;
+
+  /**
+   * Returns the index within the script of the last character where
+   * the error occurred.
+   */
+  int GetEndPosition() const;
+
+  /**
+   * Returns the index within the line of the first character where
+   * the error occurred.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", int GetStartColumn() const);
+  V8_WARN_UNUSED_RESULT Maybe<int> GetStartColumn(Local<Context> context) const;
+
+  /**
+   * Returns the index within the line of the last character where
+   * the error occurred.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", int GetEndColumn() const);
+  V8_WARN_UNUSED_RESULT Maybe<int> GetEndColumn(Local<Context> context) const;
+
+  /**
+   * Passes on the value set by the embedder when it fed the script from which
+   * this Message was generated to V8.
+   */
+  bool IsSharedCrossOrigin() const;
+  bool IsOpaque() const;
+
+  // TODO(1245381): Print to a string instead of on a FILE.
+  static void PrintCurrentStackTrace(Isolate* isolate, FILE* out);
+
+  static const int kNoLineNumberInfo = 0;
+  static const int kNoColumnInfo = 0;
+  static const int kNoScriptIdInfo = 0;
+};
+
+
+/**
+ * Representation of a JavaScript stack trace. The information collected is a
+ * snapshot of the execution stack and the information remains valid after
+ * execution continues.
+ */
+class V8_EXPORT StackTrace {
+ public:
+  /**
+   * Flags that determine what information is placed captured for each
+   * StackFrame when grabbing the current stack trace.
+   */
+  enum StackTraceOptions {
+    kLineNumber = 1,
+    kColumnOffset = 1 << 1 | kLineNumber,
+    kScriptName = 1 << 2,
+    kFunctionName = 1 << 3,
+    kIsEval = 1 << 4,
+    kIsConstructor = 1 << 5,
+    kScriptNameOrSourceURL = 1 << 6,
+    kScriptId = 1 << 7,
+    kExposeFramesAcrossSecurityOrigins = 1 << 8,
+    kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName,
+    kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL
+  };
+
+  /**
+   * Returns a StackFrame at a particular index.
+   */
+  Local<StackFrame> GetFrame(uint32_t index) const;
+
+  /**
+   * Returns the number of StackFrames.
+   */
+  int GetFrameCount() const;
+
+  /**
+   * Returns StackTrace as a v8::Array that contains StackFrame objects.
+   */
+  Local<Array> AsArray();
+
+  /**
+   * Grab a snapshot of the current JavaScript execution stack.
+   *
+   * \param frame_limit The maximum number of stack frames we want to capture.
+   * \param options Enumerates the set of things we will capture for each
+   *   StackFrame.
+   */
+  static Local<StackTrace> CurrentStackTrace(
+      Isolate* isolate,
+      int frame_limit,
+      StackTraceOptions options = kOverview);
+};
+
+
+/**
+ * A single JavaScript stack frame.
+ */
+class V8_EXPORT StackFrame {
+ public:
+  /**
+   * Returns the number, 1-based, of the line for the associate function call.
+   * This method will return Message::kNoLineNumberInfo if it is unable to
+   * retrieve the line number, or if kLineNumber was not passed as an option
+   * when capturing the StackTrace.
+   */
+  int GetLineNumber() const;
+
+  /**
+   * Returns the 1-based column offset on the line for the associated function
+   * call.
+   * This method will return Message::kNoColumnInfo if it is unable to retrieve
+   * the column number, or if kColumnOffset was not passed as an option when
+   * capturing the StackTrace.
+   */
+  int GetColumn() const;
+
+  /**
+   * Returns the id of the script for the function for this StackFrame.
+   * This method will return Message::kNoScriptIdInfo if it is unable to
+   * retrieve the script id, or if kScriptId was not passed as an option when
+   * capturing the StackTrace.
+   */
+  int GetScriptId() const;
+
+  /**
+   * Returns the name of the resource that contains the script for the
+   * function for this StackFrame.
+   */
+  Local<String> GetScriptName() const;
+
+  /**
+   * Returns the name of the resource that contains the script for the
+   * function for this StackFrame or sourceURL value if the script name
+   * is undefined and its source ends with //# sourceURL=... string or
+   * deprecated //@ sourceURL=... string.
+   */
+  Local<String> GetScriptNameOrSourceURL() const;
+
+  /**
+   * Returns the name of the function associated with this stack frame.
+   */
+  Local<String> GetFunctionName() const;
+
+  /**
+   * Returns whether or not the associated function is compiled via a call to
+   * eval().
+   */
+  bool IsEval() const;
+
+  /**
+   * Returns whether or not the associated function is called as a
+   * constructor via "new".
+   */
+  bool IsConstructor() const;
+};
+
+
+// A StateTag represents a possible state of the VM.
+enum StateTag { JS, GC, COMPILER, OTHER, EXTERNAL, IDLE };
+
+
+// A RegisterState represents the current state of registers used
+// by the sampling profiler API.
+struct RegisterState {
+  RegisterState() : pc(NULL), sp(NULL), fp(NULL) {}
+  void* pc;  // Instruction pointer.
+  void* sp;  // Stack pointer.
+  void* fp;  // Frame pointer.
+};
+
+
+// The output structure filled up by GetStackSample API function.
+struct SampleInfo {
+  size_t frames_count;
+  StateTag vm_state;
+};
+
+
+/**
+ * A JSON Parser.
+ */
+class V8_EXPORT JSON {
+ public:
+  /**
+   * Tries to parse the string |json_string| and returns it as value if
+   * successful.
+   *
+   * \param json_string The string to parse.
+   * \return The corresponding value if successfully parsed.
+   */
+  static V8_DEPRECATE_SOON("Use maybe version",
+                           Local<Value> Parse(Local<String> json_string));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Value> Parse(
+      Isolate* isolate, Local<String> json_string);
+};
+
+
+/**
+ * A map whose keys are referenced weakly. It is similar to JavaScript WeakMap
+ * but can be created without entering a v8::Context and hence shouldn't
+ * escape to JavaScript.
+ */
+class V8_EXPORT NativeWeakMap : public Data {
+ public:
+  static Local<NativeWeakMap> New(Isolate* isolate);
+  void Set(Local<Value> key, Local<Value> value);
+  Local<Value> Get(Local<Value> key);
+  bool Has(Local<Value> key);
+  bool Delete(Local<Value> key);
+};
+
+
+// --- Value ---
+
+
+/**
+ * The superclass of all JavaScript values and objects.
+ */
+class V8_EXPORT Value : public Data {
+ public:
+  /**
+   * Returns true if this value is the undefined value.  See ECMA-262
+   * 4.3.10.
+   */
+  V8_INLINE bool IsUndefined() const;
+
+  /**
+   * Returns true if this value is the null value.  See ECMA-262
+   * 4.3.11.
+   */
+  V8_INLINE bool IsNull() const;
+
+   /**
+   * Returns true if this value is true.
+   */
+  bool IsTrue() const;
+
+  /**
+   * Returns true if this value is false.
+   */
+  bool IsFalse() const;
+
+  /**
+   * Returns true if this value is a symbol or a string.
+   * This is an experimental feature.
+   */
+  bool IsName() const;
+
+  /**
+   * Returns true if this value is an instance of the String type.
+   * See ECMA-262 8.4.
+   */
+  V8_INLINE bool IsString() const;
+
+  /**
+   * Returns true if this value is a symbol.
+   * This is an experimental feature.
+   */
+  bool IsSymbol() const;
+
+  /**
+   * Returns true if this value is a function.
+   */
+  bool IsFunction() const;
+
+  /**
+   * Returns true if this value is an array.
+   */
+  bool IsArray() const;
+
+  /**
+   * Returns true if this value is an object.
+   */
+  bool IsObject() const;
+
+  /**
+   * Returns true if this value is boolean.
+   */
+  bool IsBoolean() const;
+
+  /**
+   * Returns true if this value is a number.
+   */
+  bool IsNumber() const;
+
+  /**
+   * Returns true if this value is external.
+   */
+  bool IsExternal() const;
+
+  /**
+   * Returns true if this value is a 32-bit signed integer.
+   */
+  bool IsInt32() const;
+
+  /**
+   * Returns true if this value is a 32-bit unsigned integer.
+   */
+  bool IsUint32() const;
+
+  /**
+   * Returns true if this value is a Date.
+   */
+  bool IsDate() const;
+
+  /**
+   * Returns true if this value is an Arguments object.
+   */
+  bool IsArgumentsObject() const;
+
+  /**
+   * Returns true if this value is a Boolean object.
+   */
+  bool IsBooleanObject() const;
+
+  /**
+   * Returns true if this value is a Number object.
+   */
+  bool IsNumberObject() const;
+
+  /**
+   * Returns true if this value is a String object.
+   */
+  bool IsStringObject() const;
+
+  /**
+   * Returns true if this value is a Symbol object.
+   * This is an experimental feature.
+   */
+  bool IsSymbolObject() const;
+
+  /**
+   * Returns true if this value is a NativeError.
+   */
+  bool IsNativeError() const;
+
+  /**
+   * Returns true if this value is a RegExp.
+   */
+  bool IsRegExp() const;
+
+  /**
+   * Returns true if this value is a Generator function.
+   * This is an experimental feature.
+   */
+  bool IsGeneratorFunction() const;
+
+  /**
+   * Returns true if this value is a Generator object (iterator).
+   * This is an experimental feature.
+   */
+  bool IsGeneratorObject() const;
+
+  /**
+   * Returns true if this value is a Promise.
+   * This is an experimental feature.
+   */
+  bool IsPromise() const;
+
+  /**
+   * Returns true if this value is a Map.
+   */
+  bool IsMap() const;
+
+  /**
+   * Returns true if this value is a Set.
+   */
+  bool IsSet() const;
+
+  /**
+   * Returns true if this value is a Map Iterator.
+   */
+  bool IsMapIterator() const;
+
+  /**
+   * Returns true if this value is a Set Iterator.
+   */
+  bool IsSetIterator() const;
+
+  /**
+   * Returns true if this value is a WeakMap.
+   */
+  bool IsWeakMap() const;
+
+  /**
+   * Returns true if this value is a WeakSet.
+   */
+  bool IsWeakSet() const;
+
+  /**
+   * Returns true if this value is an ArrayBuffer.
+   * This is an experimental feature.
+   */
+  bool IsArrayBuffer() const;
+
+  /**
+   * Returns true if this value is an ArrayBufferView.
+   * This is an experimental feature.
+   */
+  bool IsArrayBufferView() const;
+
+  /**
+   * Returns true if this value is one of TypedArrays.
+   * This is an experimental feature.
+   */
+  bool IsTypedArray() const;
+
+  /**
+   * Returns true if this value is an Uint8Array.
+   * This is an experimental feature.
+   */
+  bool IsUint8Array() const;
+
+  /**
+   * Returns true if this value is an Uint8ClampedArray.
+   * This is an experimental feature.
+   */
+  bool IsUint8ClampedArray() const;
+
+  /**
+   * Returns true if this value is an Int8Array.
+   * This is an experimental feature.
+   */
+  bool IsInt8Array() const;
+
+  /**
+   * Returns true if this value is an Uint16Array.
+   * This is an experimental feature.
+   */
+  bool IsUint16Array() const;
+
+  /**
+   * Returns true if this value is an Int16Array.
+   * This is an experimental feature.
+   */
+  bool IsInt16Array() const;
+
+  /**
+   * Returns true if this value is an Uint32Array.
+   * This is an experimental feature.
+   */
+  bool IsUint32Array() const;
+
+  /**
+   * Returns true if this value is an Int32Array.
+   * This is an experimental feature.
+   */
+  bool IsInt32Array() const;
+
+  /**
+   * Returns true if this value is a Float32Array.
+   * This is an experimental feature.
+   */
+  bool IsFloat32Array() const;
+
+  /**
+   * Returns true if this value is a Float64Array.
+   * This is an experimental feature.
+   */
+  bool IsFloat64Array() const;
+
+  /**
+   * Returns true if this value is a SIMD Float32x4.
+   * This is an experimental feature.
+   */
+  bool IsFloat32x4() const;
+
+  /**
+   * Returns true if this value is a DataView.
+   * This is an experimental feature.
+   */
+  bool IsDataView() const;
+
+  /**
+   * Returns true if this value is a SharedArrayBuffer.
+   * This is an experimental feature.
+   */
+  bool IsSharedArrayBuffer() const;
+
+
+  V8_WARN_UNUSED_RESULT MaybeLocal<Boolean> ToBoolean(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<Number> ToNumber(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<String> ToString(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<String> ToDetailString(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<Object> ToObject(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<Integer> ToInteger(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<Uint32> ToUint32(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT MaybeLocal<Int32> ToInt32(Local<Context> context) const;
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Boolean> ToBoolean(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Number> ToNumber(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<String> ToString(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<String> ToDetailString(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Object> ToObject(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Integer> ToInteger(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Uint32> ToUint32(Isolate* isolate) const);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Int32> ToInt32(Isolate* isolate) const);
+
+  inline V8_DEPRECATE_SOON("Use maybe version",
+                           Local<Boolean> ToBoolean() const);
+  inline V8_DEPRECATE_SOON("Use maybe version", Local<Number> ToNumber() const);
+  inline V8_DEPRECATE_SOON("Use maybe version", Local<String> ToString() const);
+  inline V8_DEPRECATE_SOON("Use maybe version",
+                           Local<String> ToDetailString() const);
+  inline V8_DEPRECATE_SOON("Use maybe version", Local<Object> ToObject() const);
+  inline V8_DEPRECATE_SOON("Use maybe version",
+                           Local<Integer> ToInteger() const);
+  inline V8_DEPRECATE_SOON("Use maybe version", Local<Uint32> ToUint32() const);
+  inline V8_DEPRECATE_SOON("Use maybe version", Local<Int32> ToInt32() const);
+
+  /**
+   * Attempts to convert a string to an array index.
+   * Returns an empty handle if the conversion fails.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", Local<Uint32> ToArrayIndex() const);
+  V8_WARN_UNUSED_RESULT MaybeLocal<Uint32> ToArrayIndex(
+      Local<Context> context) const;
+
+  V8_WARN_UNUSED_RESULT Maybe<bool> BooleanValue(Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT Maybe<double> NumberValue(Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT Maybe<int64_t> IntegerValue(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT Maybe<uint32_t> Uint32Value(
+      Local<Context> context) const;
+  V8_WARN_UNUSED_RESULT Maybe<int32_t> Int32Value(Local<Context> context) const;
+
+  V8_DEPRECATE_SOON("Use maybe version", bool BooleanValue() const);
+  V8_DEPRECATE_SOON("Use maybe version", double NumberValue() const);
+  V8_DEPRECATE_SOON("Use maybe version", int64_t IntegerValue() const);
+  V8_DEPRECATE_SOON("Use maybe version", uint32_t Uint32Value() const);
+  V8_DEPRECATE_SOON("Use maybe version", int32_t Int32Value() const);
+
+  /** JS == */
+  V8_DEPRECATE_SOON("Use maybe version", bool Equals(Local<Value> that) const);
+  V8_WARN_UNUSED_RESULT Maybe<bool> Equals(Local<Context> context,
+                                           Local<Value> that) const;
+  bool StrictEquals(Local<Value> that) const;
+  bool SameValue(Local<Value> that) const;
+
+  template <class T> V8_INLINE static Value* Cast(T* value);
+
+ private:
+  V8_INLINE bool QuickIsUndefined() const;
+  V8_INLINE bool QuickIsNull() const;
+  V8_INLINE bool QuickIsString() const;
+  bool FullIsUndefined() const;
+  bool FullIsNull() const;
+  bool FullIsString() const;
+};
+
+
+/**
+ * The superclass of primitive values.  See ECMA-262 4.3.2.
+ */
+class V8_EXPORT Primitive : public Value { };
+
+
+/**
+ * A primitive boolean value (ECMA-262, 4.3.14).  Either the true
+ * or false value.
+ */
+class V8_EXPORT Boolean : public Primitive {
+ public:
+  bool Value() const;
+  V8_INLINE static Boolean* Cast(v8::Value* obj);
+  V8_INLINE static Local<Boolean> New(Isolate* isolate, bool value);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A superclass for symbols and strings.
+ */
+class V8_EXPORT Name : public Primitive {
+ public:
+  /**
+   * Returns the identity hash for this object. The current implementation
+   * uses an inline property on the object to store the identity hash.
+   *
+   * The return value will never be 0. Also, it is not guaranteed to be
+   * unique.
+   */
+  int GetIdentityHash();
+
+  V8_INLINE static Name* Cast(v8::Value* obj);
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+enum class NewStringType { kNormal, kInternalized };
+
+
+/**
+ * A JavaScript string value (ECMA-262, 4.3.17).
+ */
+class V8_EXPORT String : public Name {
+ public:
+  static const int kMaxLength = (1 << 28) - 16;
+
+  enum Encoding {
+    UNKNOWN_ENCODING = 0x1,
+    TWO_BYTE_ENCODING = 0x0,
+    ONE_BYTE_ENCODING = 0x4
+  };
+  /**
+   * Returns the number of characters in this string.
+   */
+  int Length() const;
+
+  /**
+   * Returns the number of bytes in the UTF-8 encoded
+   * representation of this string.
+   */
+  int Utf8Length() const;
+
+  /**
+   * Returns whether this string is known to contain only one byte data.
+   * Does not read the string.
+   * False negatives are possible.
+   */
+  bool IsOneByte() const;
+
+  /**
+   * Returns whether this string contain only one byte data.
+   * Will read the entire string in some cases.
+   */
+  bool ContainsOnlyOneByte() const;
+
+  /**
+   * Write the contents of the string to an external buffer.
+   * If no arguments are given, expects the buffer to be large
+   * enough to hold the entire string and NULL terminator. Copies
+   * the contents of the string and the NULL terminator into the
+   * buffer.
+   *
+   * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop
+   * before the end of the buffer.
+   *
+   * Copies up to length characters into the output buffer.
+   * Only null-terminates if there is enough space in the buffer.
+   *
+   * \param buffer The buffer into which the string will be copied.
+   * \param start The starting position within the string at which
+   * copying begins.
+   * \param length The number of characters to copy from the string.  For
+   *    WriteUtf8 the number of bytes in the buffer.
+   * \param nchars_ref The number of characters written, can be NULL.
+   * \param options Various options that might affect performance of this or
+   *    subsequent operations.
+   * \return The number of characters copied to the buffer excluding the null
+   *    terminator.  For WriteUtf8: The number of bytes copied to the buffer
+   *    including the null terminator (if written).
+   */
+  enum WriteOptions {
+    NO_OPTIONS = 0,
+    HINT_MANY_WRITES_EXPECTED = 1,
+    NO_NULL_TERMINATION = 2,
+    PRESERVE_ONE_BYTE_NULL = 4,
+    // Used by WriteUtf8 to replace orphan surrogate code units with the
+    // unicode replacement character. Needs to be set to guarantee valid UTF-8
+    // output.
+    REPLACE_INVALID_UTF8 = 8
+  };
+
+  // 16-bit character codes.
+  int Write(uint16_t* buffer,
+            int start = 0,
+            int length = -1,
+            int options = NO_OPTIONS) const;
+  // One byte characters.
+  int WriteOneByte(uint8_t* buffer,
+                   int start = 0,
+                   int length = -1,
+                   int options = NO_OPTIONS) const;
+  // UTF-8 encoded characters.
+  int WriteUtf8(char* buffer,
+                int length = -1,
+                int* nchars_ref = NULL,
+                int options = NO_OPTIONS) const;
+
+  /**
+   * A zero length string.
+   */
+  V8_INLINE static v8::Local<v8::String> Empty(Isolate* isolate);
+
+  /**
+   * Returns true if the string is external
+   */
+  bool IsExternal() const;
+
+  /**
+   * Returns true if the string is both external and one-byte.
+   */
+  bool IsExternalOneByte() const;
+
+  class V8_EXPORT ExternalStringResourceBase {  // NOLINT
+   public:
+    virtual ~ExternalStringResourceBase() {}
+
+   protected:
+    ExternalStringResourceBase() {}
+
+    /**
+     * Internally V8 will call this Dispose method when the external string
+     * resource is no longer needed. The default implementation will use the
+     * delete operator. This method can be overridden in subclasses to
+     * control how allocated external string resources are disposed.
+     */
+    virtual void Dispose() { delete this; }
+
+   private:
+    // Disallow copying and assigning.
+    ExternalStringResourceBase(const ExternalStringResourceBase&);
+    void operator=(const ExternalStringResourceBase&);
+
+    friend class v8::internal::Heap;
+  };
+
+  /**
+   * An ExternalStringResource is a wrapper around a two-byte string
+   * buffer that resides outside V8's heap. Implement an
+   * ExternalStringResource to manage the life cycle of the underlying
+   * buffer.  Note that the string data must be immutable.
+   */
+  class V8_EXPORT ExternalStringResource
+      : public ExternalStringResourceBase {
+   public:
+    /**
+     * Override the destructor to manage the life cycle of the underlying
+     * buffer.
+     */
+    virtual ~ExternalStringResource() {}
+
+    /**
+     * The string data from the underlying buffer.
+     */
+    virtual const uint16_t* data() const = 0;
+
+    /**
+     * The length of the string. That is, the number of two-byte characters.
+     */
+    virtual size_t length() const = 0;
+
+   protected:
+    ExternalStringResource() {}
+  };
+
+  /**
+   * An ExternalOneByteStringResource is a wrapper around an one-byte
+   * string buffer that resides outside V8's heap. Implement an
+   * ExternalOneByteStringResource to manage the life cycle of the
+   * underlying buffer.  Note that the string data must be immutable
+   * and that the data must be Latin-1 and not UTF-8, which would require
+   * special treatment internally in the engine and do not allow efficient
+   * indexing.  Use String::New or convert to 16 bit data for non-Latin1.
+   */
+
+  class V8_EXPORT ExternalOneByteStringResource
+      : public ExternalStringResourceBase {
+   public:
+    /**
+     * Override the destructor to manage the life cycle of the underlying
+     * buffer.
+     */
+    virtual ~ExternalOneByteStringResource() {}
+    /** The string data from the underlying buffer.*/
+    virtual const char* data() const = 0;
+    /** The number of Latin-1 characters in the string.*/
+    virtual size_t length() const = 0;
+   protected:
+    ExternalOneByteStringResource() {}
+  };
+
+  /**
+   * If the string is an external string, return the ExternalStringResourceBase
+   * regardless of the encoding, otherwise return NULL.  The encoding of the
+   * string is returned in encoding_out.
+   */
+  V8_INLINE ExternalStringResourceBase* GetExternalStringResourceBase(
+      Encoding* encoding_out) const;
+
+  /**
+   * Get the ExternalStringResource for an external string.  Returns
+   * NULL if IsExternal() doesn't return true.
+   */
+  V8_INLINE ExternalStringResource* GetExternalStringResource() const;
+
+  /**
+   * Get the ExternalOneByteStringResource for an external one-byte string.
+   * Returns NULL if IsExternalOneByte() doesn't return true.
+   */
+  const ExternalOneByteStringResource* GetExternalOneByteStringResource() const;
+
+  V8_INLINE static String* Cast(v8::Value* obj);
+
+  // TODO(dcarney): remove with deprecation of New functions.
+  enum NewStringType {
+    kNormalString = static_cast<int>(v8::NewStringType::kNormal),
+    kInternalizedString = static_cast<int>(v8::NewStringType::kInternalized)
+  };
+
+  /** Allocates a new string from UTF-8 data.*/
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<String> NewFromUtf8(Isolate* isolate, const char* data,
+                                NewStringType type = kNormalString,
+                                int length = -1));
+
+  /** Allocates a new string from UTF-8 data. Only returns an empty value when
+   * length > kMaxLength. **/
+  static V8_WARN_UNUSED_RESULT MaybeLocal<String> NewFromUtf8(
+      Isolate* isolate, const char* data, v8::NewStringType type,
+      int length = -1);
+
+  /** Allocates a new string from Latin-1 data.*/
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<String> NewFromOneByte(Isolate* isolate, const uint8_t* data,
+                                   NewStringType type = kNormalString,
+                                   int length = -1));
+
+  /** Allocates a new string from Latin-1 data.  Only returns an empty value
+   * when length > kMaxLength. **/
+  static V8_WARN_UNUSED_RESULT MaybeLocal<String> NewFromOneByte(
+      Isolate* isolate, const uint8_t* data, v8::NewStringType type,
+      int length = -1);
+
+  /** Allocates a new string from UTF-16 data.*/
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<String> NewFromTwoByte(Isolate* isolate, const uint16_t* data,
+                                   NewStringType type = kNormalString,
+                                   int length = -1));
+
+  /** Allocates a new string from UTF-16 data. Only returns an empty value when
+   * length > kMaxLength. **/
+  static V8_WARN_UNUSED_RESULT MaybeLocal<String> NewFromTwoByte(
+      Isolate* isolate, const uint16_t* data, v8::NewStringType type,
+      int length = -1);
+
+  /**
+   * Creates a new string by concatenating the left and the right strings
+   * passed in as parameters.
+   */
+  static Local<String> Concat(Local<String> left, Local<String> right);
+
+  /**
+   * Creates a new external string using the data defined in the given
+   * resource. When the external string is no longer live on V8's heap the
+   * resource will be disposed by calling its Dispose method. The caller of
+   * this function should not otherwise delete or modify the resource. Neither
+   * should the underlying buffer be deallocated or modified except through the
+   * destructor of the external string resource.
+   */
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<String> NewExternal(Isolate* isolate,
+                                ExternalStringResource* resource));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<String> NewExternalTwoByte(
+      Isolate* isolate, ExternalStringResource* resource);
+
+  /**
+   * Associate an external string resource with this string by transforming it
+   * in place so that existing references to this string in the JavaScript heap
+   * will use the external string resource. The external string resource's
+   * character contents need to be equivalent to this string.
+   * Returns true if the string has been changed to be an external string.
+   * The string is not modified if the operation fails. See NewExternal for
+   * information on the lifetime of the resource.
+   */
+  bool MakeExternal(ExternalStringResource* resource);
+
+  /**
+   * Creates a new external string using the one-byte data defined in the given
+   * resource. When the external string is no longer live on V8's heap the
+   * resource will be disposed by calling its Dispose method. The caller of
+   * this function should not otherwise delete or modify the resource. Neither
+   * should the underlying buffer be deallocated or modified except through the
+   * destructor of the external string resource.
+   */
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<String> NewExternal(Isolate* isolate,
+                                ExternalOneByteStringResource* resource));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<String> NewExternalOneByte(
+      Isolate* isolate, ExternalOneByteStringResource* resource);
+
+  /**
+   * Associate an external string resource with this string by transforming it
+   * in place so that existing references to this string in the JavaScript heap
+   * will use the external string resource. The external string resource's
+   * character contents need to be equivalent to this string.
+   * Returns true if the string has been changed to be an external string.
+   * The string is not modified if the operation fails. See NewExternal for
+   * information on the lifetime of the resource.
+   */
+  bool MakeExternal(ExternalOneByteStringResource* resource);
+
+  /**
+   * Returns true if this string can be made external.
+   */
+  bool CanMakeExternal();
+
+  /**
+   * Converts an object to a UTF-8-encoded character array.  Useful if
+   * you want to print the object.  If conversion to a string fails
+   * (e.g. due to an exception in the toString() method of the object)
+   * then the length() method returns 0 and the * operator returns
+   * NULL.
+   */
+  class V8_EXPORT Utf8Value {
+   public:
+    explicit Utf8Value(Local<v8::Value> obj);
+    ~Utf8Value();
+    char* operator*() { return str_; }
+    const char* operator*() const { return str_; }
+    int length() const { return length_; }
+   private:
+    char* str_;
+    int length_;
+
+    // Disallow copying and assigning.
+    Utf8Value(const Utf8Value&);
+    void operator=(const Utf8Value&);
+  };
+
+  /**
+   * Converts an object to a two-byte string.
+   * If conversion to a string fails (eg. due to an exception in the toString()
+   * method of the object) then the length() method returns 0 and the * operator
+   * returns NULL.
+   */
+  class V8_EXPORT Value {
+   public:
+    explicit Value(Local<v8::Value> obj);
+    ~Value();
+    uint16_t* operator*() { return str_; }
+    const uint16_t* operator*() const { return str_; }
+    int length() const { return length_; }
+   private:
+    uint16_t* str_;
+    int length_;
+
+    // Disallow copying and assigning.
+    Value(const Value&);
+    void operator=(const Value&);
+  };
+
+ private:
+  void VerifyExternalStringResourceBase(ExternalStringResourceBase* v,
+                                        Encoding encoding) const;
+  void VerifyExternalStringResource(ExternalStringResource* val) const;
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript symbol (ECMA-262 edition 6)
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8_EXPORT Symbol : public Name {
+ public:
+  // Returns the print name string of the symbol, or undefined if none.
+  Local<Value> Name() const;
+
+  // Create a symbol. If name is not empty, it will be used as the description.
+  static Local<Symbol> New(
+      Isolate *isolate, Local<String> name = Local<String>());
+
+  // Access global symbol registry.
+  // Note that symbols created this way are never collected, so
+  // they should only be used for statically fixed properties.
+  // Also, there is only one global name space for the names used as keys.
+  // To minimize the potential for clashes, use qualified names as keys.
+  static Local<Symbol> For(Isolate *isolate, Local<String> name);
+
+  // Retrieve a global symbol. Similar to |For|, but using a separate
+  // registry that is not accessible by (and cannot clash with) JavaScript code.
+  static Local<Symbol> ForApi(Isolate *isolate, Local<String> name);
+
+  // Well-known symbols
+  static Local<Symbol> GetIterator(Isolate* isolate);
+  static Local<Symbol> GetUnscopables(Isolate* isolate);
+  static Local<Symbol> GetToStringTag(Isolate* isolate);
+  static Local<Symbol> GetIsConcatSpreadable(Isolate* isolate);
+
+  V8_INLINE static Symbol* Cast(v8::Value* obj);
+
+ private:
+  Symbol();
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript number value (ECMA-262, 4.3.20)
+ */
+class V8_EXPORT Number : public Primitive {
+ public:
+  double Value() const;
+  static Local<Number> New(Isolate* isolate, double value);
+  V8_INLINE static Number* Cast(v8::Value* obj);
+ private:
+  Number();
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a signed integer.
+ */
+class V8_EXPORT Integer : public Number {
+ public:
+  static Local<Integer> New(Isolate* isolate, int32_t value);
+  static Local<Integer> NewFromUnsigned(Isolate* isolate, uint32_t value);
+  int64_t Value() const;
+  V8_INLINE static Integer* Cast(v8::Value* obj);
+ private:
+  Integer();
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a 32-bit signed integer.
+ */
+class V8_EXPORT Int32 : public Integer {
+ public:
+  int32_t Value() const;
+  V8_INLINE static Int32* Cast(v8::Value* obj);
+
+ private:
+  Int32();
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a 32-bit unsigned integer.
+ */
+class V8_EXPORT Uint32 : public Integer {
+ public:
+  uint32_t Value() const;
+  V8_INLINE static Uint32* Cast(v8::Value* obj);
+
+ private:
+  Uint32();
+  static void CheckCast(v8::Value* obj);
+};
+
+
+enum PropertyAttribute {
+  None       = 0,
+  ReadOnly   = 1 << 0,
+  DontEnum   = 1 << 1,
+  DontDelete = 1 << 2
+};
+
+/**
+ * Accessor[Getter|Setter] are used as callback functions when
+ * setting|getting a particular property. See Object and ObjectTemplate's
+ * method SetAccessor.
+ */
+typedef void (*AccessorGetterCallback)(
+    Local<String> property,
+    const PropertyCallbackInfo<Value>& info);
+typedef void (*AccessorNameGetterCallback)(
+    Local<Name> property,
+    const PropertyCallbackInfo<Value>& info);
+
+
+typedef void (*AccessorSetterCallback)(
+    Local<String> property,
+    Local<Value> value,
+    const PropertyCallbackInfo<void>& info);
+typedef void (*AccessorNameSetterCallback)(
+    Local<Name> property,
+    Local<Value> value,
+    const PropertyCallbackInfo<void>& info);
+
+
+/**
+ * Access control specifications.
+ *
+ * Some accessors should be accessible across contexts.  These
+ * accessors have an explicit access control parameter which specifies
+ * the kind of cross-context access that should be allowed.
+ *
+ * TODO(dcarney): Remove PROHIBITS_OVERWRITING as it is now unused.
+ */
+enum AccessControl {
+  DEFAULT               = 0,
+  ALL_CAN_READ          = 1,
+  ALL_CAN_WRITE         = 1 << 1,
+  PROHIBITS_OVERWRITING = 1 << 2
+};
+
+
+/**
+ * A JavaScript object (ECMA-262, 4.3.3)
+ */
+class V8_EXPORT Object : public Value {
+ public:
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool Set(Local<Value> key, Local<Value> value));
+  V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context,
+                                        Local<Value> key, Local<Value> value);
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool Set(uint32_t index, Local<Value> value));
+  V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context, uint32_t index,
+                                        Local<Value> value);
+
+  // Implements CreateDataProperty (ECMA-262, 7.3.4).
+  //
+  // Defines a configurable, writable, enumerable property with the given value
+  // on the object unless the property already exists and is not configurable
+  // or the object is not extensible.
+  //
+  // Returns true on success.
+  V8_WARN_UNUSED_RESULT Maybe<bool> CreateDataProperty(Local<Context> context,
+                                                       Local<Name> key,
+                                                       Local<Value> value);
+  V8_WARN_UNUSED_RESULT Maybe<bool> CreateDataProperty(Local<Context> context,
+                                                       uint32_t index,
+                                                       Local<Value> value);
+
+  // Implements DefineOwnProperty.
+  //
+  // In general, CreateDataProperty will be faster, however, does not allow
+  // for specifying attributes.
+  //
+  // Returns true on success.
+  V8_WARN_UNUSED_RESULT Maybe<bool> DefineOwnProperty(
+      Local<Context> context, Local<Name> key, Local<Value> value,
+      PropertyAttribute attributes = None);
+
+  // Sets an own property on this object bypassing interceptors and
+  // overriding accessors or read-only properties.
+  //
+  // Note that if the object has an interceptor the property will be set
+  // locally, but since the interceptor takes precedence the local property
+  // will only be returned if the interceptor doesn't return a value.
+  //
+  // Note also that this only works for named properties.
+  V8_DEPRECATE_SOON("Use CreateDataProperty",
+                    bool ForceSet(Local<Value> key, Local<Value> value,
+                                  PropertyAttribute attribs = None));
+  V8_DEPRECATE_SOON("Use CreateDataProperty",
+                    Maybe<bool> ForceSet(Local<Context> context,
+                                         Local<Value> key, Local<Value> value,
+                                         PropertyAttribute attribs = None));
+
+  V8_DEPRECATE_SOON("Use maybe version", Local<Value> Get(Local<Value> key));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
+                                              Local<Value> key);
+
+  V8_DEPRECATE_SOON("Use maybe version", Local<Value> Get(uint32_t index));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
+                                              uint32_t index);
+
+  /**
+   * Gets the property attributes of a property which can be None or
+   * any combination of ReadOnly, DontEnum and DontDelete. Returns
+   * None when the property doesn't exist.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    PropertyAttribute GetPropertyAttributes(Local<Value> key));
+  V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute> GetPropertyAttributes(
+      Local<Context> context, Local<Value> key);
+
+  /**
+   * Returns Object.getOwnPropertyDescriptor as per ES5 section 15.2.3.3.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Value> GetOwnPropertyDescriptor(Local<String> key));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> GetOwnPropertyDescriptor(
+      Local<Context> context, Local<String> key);
+
+  V8_DEPRECATE_SOON("Use maybe version", bool Has(Local<Value> key));
+  V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,
+                                        Local<Value> key);
+
+  V8_DEPRECATE_SOON("Use maybe version", bool Delete(Local<Value> key));
+  // TODO(dcarney): mark V8_WARN_UNUSED_RESULT
+  Maybe<bool> Delete(Local<Context> context, Local<Value> key);
+
+  V8_DEPRECATE_SOON("Use maybe version", bool Has(uint32_t index));
+  V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context, uint32_t index);
+
+  V8_DEPRECATE_SOON("Use maybe version", bool Delete(uint32_t index));
+  // TODO(dcarney): mark V8_WARN_UNUSED_RESULT
+  Maybe<bool> Delete(Local<Context> context, uint32_t index);
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool SetAccessor(Local<String> name,
+                                     AccessorGetterCallback getter,
+                                     AccessorSetterCallback setter = 0,
+                                     Local<Value> data = Local<Value>(),
+                                     AccessControl settings = DEFAULT,
+                                     PropertyAttribute attribute = None));
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool SetAccessor(Local<Name> name,
+                                     AccessorNameGetterCallback getter,
+                                     AccessorNameSetterCallback setter = 0,
+                                     Local<Value> data = Local<Value>(),
+                                     AccessControl settings = DEFAULT,
+                                     PropertyAttribute attribute = None));
+  // TODO(dcarney): mark V8_WARN_UNUSED_RESULT
+  Maybe<bool> SetAccessor(Local<Context> context, Local<Name> name,
+                          AccessorNameGetterCallback getter,
+                          AccessorNameSetterCallback setter = 0,
+                          MaybeLocal<Value> data = MaybeLocal<Value>(),
+                          AccessControl settings = DEFAULT,
+                          PropertyAttribute attribute = None);
+
+  void SetAccessorProperty(Local<Name> name, Local<Function> getter,
+                           Local<Function> setter = Local<Function>(),
+                           PropertyAttribute attribute = None,
+                           AccessControl settings = DEFAULT);
+
+  /**
+   * Returns an array containing the names of the enumerable properties
+   * of this object, including properties from prototype objects.  The
+   * array returned by this method contains the same values as would
+   * be enumerated by a for-in statement over this object.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", Local<Array> GetPropertyNames());
+  V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetPropertyNames(
+      Local<Context> context);
+
+  /**
+   * This function has the same functionality as GetPropertyNames but
+   * the returned array doesn't contain the names of properties from
+   * prototype objects.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", Local<Array> GetOwnPropertyNames());
+  V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetOwnPropertyNames(
+      Local<Context> context);
+
+  /**
+   * Get the prototype object.  This does not skip objects marked to
+   * be skipped by __proto__ and it does not consult the security
+   * handler.
+   */
+  Local<Value> GetPrototype();
+
+  /**
+   * Set the prototype object.  This does not skip objects marked to
+   * be skipped by __proto__ and it does not consult the security
+   * handler.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool SetPrototype(Local<Value> prototype));
+  V8_WARN_UNUSED_RESULT Maybe<bool> SetPrototype(Local<Context> context,
+                                                 Local<Value> prototype);
+
+  /**
+   * Finds an instance of the given function template in the prototype
+   * chain.
+   */
+  Local<Object> FindInstanceInPrototypeChain(Local<FunctionTemplate> tmpl);
+
+  /**
+   * Call builtin Object.prototype.toString on this object.
+   * This is different from Value::ToString() that may call
+   * user-defined toString function. This one does not.
+   */
+  V8_DEPRECATE_SOON("Use maybe version", Local<String> ObjectProtoToString());
+  V8_WARN_UNUSED_RESULT MaybeLocal<String> ObjectProtoToString(
+      Local<Context> context);
+
+  /**
+   * Returns the name of the function invoked as a constructor for this object.
+   */
+  Local<String> GetConstructorName();
+
+  /** Gets the number of internal fields for this Object. */
+  int InternalFieldCount();
+
+  /** Same as above, but works for Persistents */
+  V8_INLINE static int InternalFieldCount(
+      const PersistentBase<Object>& object) {
+    return object.val_->InternalFieldCount();
+  }
+
+  /** Gets the value from an internal field. */
+  V8_INLINE Local<Value> GetInternalField(int index);
+
+  /** Sets the value in an internal field. */
+  void SetInternalField(int index, Local<Value> value);
+
+  /**
+   * Gets a 2-byte-aligned native pointer from an internal field. This field
+   * must have been set by SetAlignedPointerInInternalField, everything else
+   * leads to undefined behavior.
+   */
+  V8_INLINE void* GetAlignedPointerFromInternalField(int index);
+
+  /** Same as above, but works for Persistents */
+  V8_INLINE static void* GetAlignedPointerFromInternalField(
+      const PersistentBase<Object>& object, int index) {
+    return object.val_->GetAlignedPointerFromInternalField(index);
+  }
+
+  /**
+   * Sets a 2-byte-aligned native pointer in an internal field. To retrieve such
+   * a field, GetAlignedPointerFromInternalField must be used, everything else
+   * leads to undefined behavior.
+   */
+  void SetAlignedPointerInInternalField(int index, void* value);
+
+  // Testers for local properties.
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool HasOwnProperty(Local<String> key));
+  V8_WARN_UNUSED_RESULT Maybe<bool> HasOwnProperty(Local<Context> context,
+                                                   Local<Name> key);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool HasRealNamedProperty(Local<String> key));
+  V8_WARN_UNUSED_RESULT Maybe<bool> HasRealNamedProperty(Local<Context> context,
+                                                         Local<Name> key);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool HasRealIndexedProperty(uint32_t index));
+  V8_WARN_UNUSED_RESULT Maybe<bool> HasRealIndexedProperty(
+      Local<Context> context, uint32_t index);
+  V8_DEPRECATE_SOON("Use maybe version",
+                    bool HasRealNamedCallbackProperty(Local<String> key));
+  V8_WARN_UNUSED_RESULT Maybe<bool> HasRealNamedCallbackProperty(
+      Local<Context> context, Local<Name> key);
+
+  /**
+   * If result.IsEmpty() no real property was located in the prototype chain.
+   * This means interceptors in the prototype chain are not called.
+   */
+  V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<Value> GetRealNamedPropertyInPrototypeChain(Local<String> key));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> GetRealNamedPropertyInPrototypeChain(
+      Local<Context> context, Local<Name> key);
+
+  /**
+   * Gets the property attributes of a real property in the prototype chain,
+   * which can be None or any combination of ReadOnly, DontEnum and DontDelete.
+   * Interceptors in the prototype chain are not called.
+   */
+  V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Maybe<PropertyAttribute> GetRealNamedPropertyAttributesInPrototypeChain(
+          Local<String> key));
+  V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute>
+  GetRealNamedPropertyAttributesInPrototypeChain(Local<Context> context,
+                                                 Local<Name> key);
+
+  /**
+   * If result.IsEmpty() no real property was located on the object or
+   * in the prototype chain.
+   * This means interceptors in the prototype chain are not called.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Value> GetRealNamedProperty(Local<String> key));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> GetRealNamedProperty(
+      Local<Context> context, Local<Name> key);
+
+  /**
+   * Gets the property attributes of a real property which can be
+   * None or any combination of ReadOnly, DontEnum and DontDelete.
+   * Interceptors in the prototype chain are not called.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Maybe<PropertyAttribute> GetRealNamedPropertyAttributes(
+                        Local<String> key));
+  V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute> GetRealNamedPropertyAttributes(
+      Local<Context> context, Local<Name> key);
+
+  /** Tests for a named lookup interceptor.*/
+  bool HasNamedLookupInterceptor();
+
+  /** Tests for an index lookup interceptor.*/
+  bool HasIndexedLookupInterceptor();
+
+  /**
+   * Returns the identity hash for this object. The current implementation
+   * uses a hidden property on the object to store the identity hash.
+   *
+   * The return value will never be 0. Also, it is not guaranteed to be
+   * unique.
+   */
+  int GetIdentityHash();
+
+  /**
+   * Access hidden properties on JavaScript objects. These properties are
+   * hidden from the executing JavaScript and only accessible through the V8
+   * C++ API. Hidden properties introduced by V8 internally (for example the
+   * identity hash) are prefixed with "v8::".
+   */
+  // TODO(dcarney): convert these to take a isolate and optionally bailout?
+  bool SetHiddenValue(Local<String> key, Local<Value> value);
+  Local<Value> GetHiddenValue(Local<String> key);
+  bool DeleteHiddenValue(Local<String> key);
+
+  /**
+   * Clone this object with a fast but shallow copy.  Values will point
+   * to the same values as the original object.
+   */
+  // TODO(dcarney): take an isolate and optionally bail out?
+  Local<Object> Clone();
+
+  /**
+   * Returns the context in which the object was created.
+   */
+  Local<Context> CreationContext();
+
+  /**
+   * Checks whether a callback is set by the
+   * ObjectTemplate::SetCallAsFunctionHandler method.
+   * When an Object is callable this method returns true.
+   */
+  bool IsCallable();
+
+  /**
+   * Call an Object as a function if a callback is set by the
+   * ObjectTemplate::SetCallAsFunctionHandler method.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Value> CallAsFunction(Local<Value> recv, int argc,
+                                                Local<Value> argv[]));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> CallAsFunction(Local<Context> context,
+                                                         Local<Value> recv,
+                                                         int argc,
+                                                         Local<Value> argv[]);
+
+  /**
+   * Call an Object as a constructor if a callback is set by the
+   * ObjectTemplate::SetCallAsFunctionHandler method.
+   * Note: This method behaves like the Function::NewInstance method.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Value> CallAsConstructor(int argc,
+                                                   Local<Value> argv[]));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> CallAsConstructor(
+      Local<Context> context, int argc, Local<Value> argv[]);
+
+  /**
+   * Return the isolate to which the Object belongs to.
+   */
+  V8_DEPRECATE_SOON("Keep track of isolate correctly", Isolate* GetIsolate());
+
+  static Local<Object> New(Isolate* isolate);
+
+  V8_INLINE static Object* Cast(Value* obj);
+
+ private:
+  Object();
+  static void CheckCast(Value* obj);
+  Local<Value> SlowGetInternalField(int index);
+  void* SlowGetAlignedPointerFromInternalField(int index);
+};
+
+
+/**
+ * An instance of the built-in array constructor (ECMA-262, 15.4.2).
+ */
+class V8_EXPORT Array : public Object {
+ public:
+  uint32_t Length() const;
+
+  /**
+   * Clones an element at index |index|.  Returns an empty
+   * handle if cloning fails (for any reason).
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Object> CloneElementAt(uint32_t index));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Object> CloneElementAt(
+      Local<Context> context, uint32_t index);
+
+  /**
+   * Creates a JavaScript array with the given length. If the length
+   * is negative the returned array will have length 0.
+   */
+  static Local<Array> New(Isolate* isolate, int length = 0);
+
+  V8_INLINE static Array* Cast(Value* obj);
+ private:
+  Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in Map constructor (ECMA-262, 6th Edition, 23.1.1).
+ */
+class V8_EXPORT Map : public Object {
+ public:
+  size_t Size() const;
+  void Clear();
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
+                                              Local<Value> key);
+  V8_WARN_UNUSED_RESULT MaybeLocal<Map> Set(Local<Context> context,
+                                            Local<Value> key,
+                                            Local<Value> value);
+  V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,
+                                        Local<Value> key);
+  V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,
+                                           Local<Value> key);
+
+  /**
+   * Returns an array of length Size() * 2, where index N is the Nth key and
+   * index N + 1 is the Nth value.
+   */
+  Local<Array> AsArray() const;
+
+  /**
+   * Creates a new empty Map.
+   */
+  static Local<Map> New(Isolate* isolate);
+
+  /**
+   * Creates a new Map containing the elements of array, which must be formatted
+   * in the same manner as the array returned from AsArray().
+   * Guaranteed to be side-effect free if the array contains no holes.
+   */
+  static V8_WARN_UNUSED_RESULT V8_DEPRECATED(
+      "Use mutation methods instead",
+      MaybeLocal<Map> FromArray(Local<Context> context, Local<Array> array));
+
+  V8_INLINE static Map* Cast(Value* obj);
+
+ private:
+  Map();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in Set constructor (ECMA-262, 6th Edition, 23.2.1).
+ */
+class V8_EXPORT Set : public Object {
+ public:
+  size_t Size() const;
+  void Clear();
+  V8_WARN_UNUSED_RESULT MaybeLocal<Set> Add(Local<Context> context,
+                                            Local<Value> key);
+  V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,
+                                        Local<Value> key);
+  V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,
+                                           Local<Value> key);
+
+  /**
+   * Returns an array of the keys in this Set.
+   */
+  Local<Array> AsArray() const;
+
+  /**
+   * Creates a new empty Set.
+   */
+  static Local<Set> New(Isolate* isolate);
+
+  /**
+   * Creates a new Set containing the items in array.
+   * Guaranteed to be side-effect free if the array contains no holes.
+   */
+  static V8_WARN_UNUSED_RESULT V8_DEPRECATED(
+      "Use mutation methods instead",
+      MaybeLocal<Set> FromArray(Local<Context> context, Local<Array> array));
+
+  V8_INLINE static Set* Cast(Value* obj);
+
+ private:
+  Set();
+  static void CheckCast(Value* obj);
+};
+
+
+template<typename T>
+class ReturnValue {
+ public:
+  template <class S> V8_INLINE ReturnValue(const ReturnValue<S>& that)
+      : value_(that.value_) {
+    TYPE_CHECK(T, S);
+  }
+  // Local setters
+  template <typename S>
+  V8_INLINE V8_DEPRECATE_SOON("Use Global<> instead",
+                              void Set(const Persistent<S>& handle));
+  template <typename S>
+  V8_INLINE void Set(const Global<S>& handle);
+  template <typename S>
+  V8_INLINE void Set(const Local<S> handle);
+  // Fast primitive setters
+  V8_INLINE void Set(bool value);
+  V8_INLINE void Set(double i);
+  V8_INLINE void Set(int32_t i);
+  V8_INLINE void Set(uint32_t i);
+  // Fast JS primitive setters
+  V8_INLINE void SetNull();
+  V8_INLINE void SetUndefined();
+  V8_INLINE void SetEmptyString();
+  // Convenience getter for Isolate
+  V8_INLINE Isolate* GetIsolate();
+
+  // Pointer setter: Uncompilable to prevent inadvertent misuse.
+  template <typename S>
+  V8_INLINE void Set(S* whatever);
+
+ private:
+  template<class F> friend class ReturnValue;
+  template<class F> friend class FunctionCallbackInfo;
+  template<class F> friend class PropertyCallbackInfo;
+  template <class F, class G, class H>
+  friend class PersistentValueMapBase;
+  V8_INLINE void SetInternal(internal::Object* value) { *value_ = value; }
+  V8_INLINE internal::Object* GetDefaultValue();
+  V8_INLINE explicit ReturnValue(internal::Object** slot);
+  internal::Object** value_;
+};
+
+
+/**
+ * The argument information given to function call callbacks.  This
+ * class provides access to information about the context of the call,
+ * including the receiver, the number and values of arguments, and
+ * the holder of the function.
+ */
+template<typename T>
+class FunctionCallbackInfo {
+ public:
+  V8_INLINE int Length() const;
+  V8_INLINE Local<Value> operator[](int i) const;
+  V8_INLINE Local<Function> Callee() const;
+  V8_INLINE Local<Object> This() const;
+  V8_INLINE Local<Object> Holder() const;
+  V8_INLINE bool IsConstructCall() const;
+  V8_INLINE Local<Value> Data() const;
+  V8_INLINE Isolate* GetIsolate() const;
+  V8_INLINE ReturnValue<T> GetReturnValue() const;
+  // This shouldn't be public, but the arm compiler needs it.
+  static const int kArgsLength = 7;
+
+ protected:
+  friend class internal::FunctionCallbackArguments;
+  friend class internal::CustomArguments<FunctionCallbackInfo>;
+  static const int kHolderIndex = 0;
+  static const int kIsolateIndex = 1;
+  static const int kReturnValueDefaultValueIndex = 2;
+  static const int kReturnValueIndex = 3;
+  static const int kDataIndex = 4;
+  static const int kCalleeIndex = 5;
+  static const int kContextSaveIndex = 6;
+
+  V8_INLINE FunctionCallbackInfo(internal::Object** implicit_args,
+                   internal::Object** values,
+                   int length,
+                   bool is_construct_call);
+  internal::Object** implicit_args_;
+  internal::Object** values_;
+  int length_;
+  int is_construct_call_;
+};
+
+
+/**
+ * The information passed to a property callback about the context
+ * of the property access.
+ */
+template<typename T>
+class PropertyCallbackInfo {
+ public:
+  V8_INLINE Isolate* GetIsolate() const;
+  V8_INLINE Local<Value> Data() const;
+  V8_INLINE Local<Object> This() const;
+  V8_INLINE Local<Object> Holder() const;
+  V8_INLINE ReturnValue<T> GetReturnValue() const;
+  // This shouldn't be public, but the arm compiler needs it.
+  static const int kArgsLength = 6;
+
+ protected:
+  friend class MacroAssembler;
+  friend class internal::PropertyCallbackArguments;
+  friend class internal::CustomArguments<PropertyCallbackInfo>;
+  static const int kHolderIndex = 0;
+  static const int kIsolateIndex = 1;
+  static const int kReturnValueDefaultValueIndex = 2;
+  static const int kReturnValueIndex = 3;
+  static const int kDataIndex = 4;
+  static const int kThisIndex = 5;
+
+  V8_INLINE PropertyCallbackInfo(internal::Object** args) : args_(args) {}
+  internal::Object** args_;
+};
+
+
+typedef void (*FunctionCallback)(const FunctionCallbackInfo<Value>& info);
+
+
+/**
+ * A JavaScript function object (ECMA-262, 15.3).
+ */
+class V8_EXPORT Function : public Object {
+ public:
+  /**
+   * Create a function in the current execution context
+   * for a given FunctionCallback.
+   */
+  static MaybeLocal<Function> New(Local<Context> context,
+                                  FunctionCallback callback,
+                                  Local<Value> data = Local<Value>(),
+                                  int length = 0);
+  static V8_DEPRECATE_SOON(
+      "Use maybe version",
+      Local<Function> New(Isolate* isolate, FunctionCallback callback,
+                          Local<Value> data = Local<Value>(), int length = 0));
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Object> NewInstance(int argc, Local<Value> argv[])
+                        const);
+  V8_WARN_UNUSED_RESULT MaybeLocal<Object> NewInstance(
+      Local<Context> context, int argc, Local<Value> argv[]) const;
+
+  V8_DEPRECATE_SOON("Use maybe version", Local<Object> NewInstance() const);
+  V8_WARN_UNUSED_RESULT MaybeLocal<Object> NewInstance(
+      Local<Context> context) const {
+    return NewInstance(context, 0, nullptr);
+  }
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Value> Call(Local<Value> recv, int argc,
+                                      Local<Value> argv[]));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> Call(Local<Context> context,
+                                               Local<Value> recv, int argc,
+                                               Local<Value> argv[]);
+
+  void SetName(Local<String> name);
+  Local<Value> GetName() const;
+
+  /**
+   * Name inferred from variable or property assignment of this function.
+   * Used to facilitate debugging and profiling of JavaScript code written
+   * in an OO style, where many functions are anonymous but are assigned
+   * to object properties.
+   */
+  Local<Value> GetInferredName() const;
+
+  /**
+   * User-defined name assigned to the "displayName" property of this function.
+   * Used to facilitate debugging and profiling of JavaScript code.
+   */
+  Local<Value> GetDisplayName() const;
+
+  /**
+   * Returns zero based line number of function body and
+   * kLineOffsetNotFound if no information available.
+   */
+  int GetScriptLineNumber() const;
+  /**
+   * Returns zero based column number of function body and
+   * kLineOffsetNotFound if no information available.
+   */
+  int GetScriptColumnNumber() const;
+
+  /**
+   * Tells whether this function is builtin.
+   */
+  bool IsBuiltin() const;
+
+  /**
+   * Returns scriptId.
+   */
+  int ScriptId() const;
+
+  /**
+   * Returns the original function if this function is bound, else returns
+   * v8::Undefined.
+   */
+  Local<Value> GetBoundFunction() const;
+
+  ScriptOrigin GetScriptOrigin() const;
+  V8_INLINE static Function* Cast(Value* obj);
+  static const int kLineOffsetNotFound;
+
+ private:
+  Function();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in Promise constructor (ES6 draft).
+ * This API is experimental. Only works with --harmony flag.
+ */
+class V8_EXPORT Promise : public Object {
+ public:
+  class V8_EXPORT Resolver : public Object {
+   public:
+    /**
+     * Create a new resolver, along with an associated promise in pending state.
+     */
+    static V8_DEPRECATE_SOON("Use maybe version",
+                             Local<Resolver> New(Isolate* isolate));
+    static V8_WARN_UNUSED_RESULT MaybeLocal<Resolver> New(
+        Local<Context> context);
+
+    /**
+     * Extract the associated promise.
+     */
+    Local<Promise> GetPromise();
+
+    /**
+     * Resolve/reject the associated promise with a given value.
+     * Ignored if the promise is no longer pending.
+     */
+    V8_DEPRECATE_SOON("Use maybe version", void Resolve(Local<Value> value));
+    // TODO(dcarney): mark V8_WARN_UNUSED_RESULT
+    Maybe<bool> Resolve(Local<Context> context, Local<Value> value);
+
+    V8_DEPRECATE_SOON("Use maybe version", void Reject(Local<Value> value));
+    // TODO(dcarney): mark V8_WARN_UNUSED_RESULT
+    Maybe<bool> Reject(Local<Context> context, Local<Value> value);
+
+    V8_INLINE static Resolver* Cast(Value* obj);
+
+   private:
+    Resolver();
+    static void CheckCast(Value* obj);
+  };
+
+  /**
+   * Register a resolution/rejection handler with a promise.
+   * The handler is given the respective resolution/rejection value as
+   * an argument. If the promise is already resolved/rejected, the handler is
+   * invoked at the end of turn.
+   */
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Promise> Chain(Local<Function> handler));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Promise> Chain(Local<Context> context,
+                                                  Local<Function> handler);
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Promise> Catch(Local<Function> handler));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Promise> Catch(Local<Context> context,
+                                                  Local<Function> handler);
+
+  V8_DEPRECATE_SOON("Use maybe version",
+                    Local<Promise> Then(Local<Function> handler));
+  V8_WARN_UNUSED_RESULT MaybeLocal<Promise> Then(Local<Context> context,
+                                                 Local<Function> handler);
+
+  /**
+   * Returns true if the promise has at least one derived promise, and
+   * therefore resolve/reject handlers (including default handler).
+   */
+  bool HasHandler();
+
+  V8_INLINE static Promise* Cast(Value* obj);
+
+ private:
+  Promise();
+  static void CheckCast(Value* obj);
+};
+
+
+#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
+// The number of required internal fields can be defined by embedder.
+#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
+#endif
+
+
+enum class ArrayBufferCreationMode { kInternalized, kExternalized };
+
+
+/**
+ * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT ArrayBuffer : public Object {
+ public:
+  /**
+   * Allocator that V8 uses to allocate |ArrayBuffer|'s memory.
+   * The allocator is a global V8 setting. It has to be set via
+   * Isolate::CreateParams.
+   *
+   * This API is experimental and may change significantly.
+   */
+  class V8_EXPORT Allocator { // NOLINT
+   public:
+    virtual ~Allocator() {}
+
+    /**
+     * Allocate |length| bytes. Return NULL if allocation is not successful.
+     * Memory should be initialized to zeroes.
+     */
+    virtual void* Allocate(size_t length) = 0;
+
+    /**
+     * Allocate |length| bytes. Return NULL if allocation is not successful.
+     * Memory does not have to be initialized.
+     */
+    virtual void* AllocateUninitialized(size_t length) = 0;
+    /**
+     * Free the memory block of size |length|, pointed to by |data|.
+     * That memory is guaranteed to be previously allocated by |Allocate|.
+     */
+    virtual void Free(void* data, size_t length) = 0;
+  };
+
+  /**
+   * The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer|
+   * returns an instance of this class, populated, with a pointer to data
+   * and byte length.
+   *
+   * The Data pointer of ArrayBuffer::Contents is always allocated with
+   * Allocator::Allocate that is set via Isolate::CreateParams.
+   *
+   * This API is experimental and may change significantly.
+   */
+  class V8_EXPORT Contents { // NOLINT
+   public:
+    Contents() : data_(NULL), byte_length_(0) {}
+
+    void* Data() const { return data_; }
+    size_t ByteLength() const { return byte_length_; }
+
+   private:
+    void* data_;
+    size_t byte_length_;
+
+    friend class ArrayBuffer;
+  };
+
+
+  /**
+   * Data length in bytes.
+   */
+  size_t ByteLength() const;
+
+  /**
+   * Create a new ArrayBuffer. Allocate |byte_length| bytes.
+   * Allocated memory will be owned by a created ArrayBuffer and
+   * will be deallocated when it is garbage-collected,
+   * unless the object is externalized.
+   */
+  static Local<ArrayBuffer> New(Isolate* isolate, size_t byte_length);
+
+  /**
+   * Create a new ArrayBuffer over an existing memory block.
+   * The created array buffer is by default immediately in externalized state.
+   * The memory block will not be reclaimed when a created ArrayBuffer
+   * is garbage-collected.
+   */
+  static Local<ArrayBuffer> New(
+      Isolate* isolate, void* data, size_t byte_length,
+      ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized);
+
+  /**
+   * Returns true if ArrayBuffer is externalized, that is, does not
+   * own its memory block.
+   */
+  bool IsExternal() const;
+
+  /**
+   * Returns true if this ArrayBuffer may be neutered.
+   */
+  bool IsNeuterable() const;
+
+  /**
+   * Neuters this ArrayBuffer and all its views (typed arrays).
+   * Neutering sets the byte length of the buffer and all typed arrays to zero,
+   * preventing JavaScript from ever accessing underlying backing store.
+   * ArrayBuffer should have been externalized and must be neuterable.
+   */
+  void Neuter();
+
+  /**
+   * Make this ArrayBuffer external. The pointer to underlying memory block
+   * and byte length are returned as |Contents| structure. After ArrayBuffer
+   * had been etxrenalized, it does no longer owns the memory block. The caller
+   * should take steps to free memory when it is no longer needed.
+   *
+   * The memory block is guaranteed to be allocated with |Allocator::Allocate|
+   * that has been set via Isolate::CreateParams.
+   */
+  Contents Externalize();
+
+  /**
+   * Get a pointer to the ArrayBuffer's underlying memory block without
+   * externalizing it. If the ArrayBuffer is not externalized, this pointer
+   * will become invalid as soon as the ArrayBuffer became garbage collected.
+   *
+   * The embedder should make sure to hold a strong reference to the
+   * ArrayBuffer while accessing this pointer.
+   *
+   * The memory block is guaranteed to be allocated with |Allocator::Allocate|.
+   */
+  Contents GetContents();
+
+  V8_INLINE static ArrayBuffer* Cast(Value* obj);
+
+  static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
+
+ private:
+  ArrayBuffer();
+  static void CheckCast(Value* obj);
+};
+
+
+#ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT
+// The number of required internal fields can be defined by embedder.
+#define V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT 2
+#endif
+
+
+/**
+ * A base class for an instance of one of "views" over ArrayBuffer,
+ * including TypedArrays and DataView (ES6 draft 15.13).
+ *
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT ArrayBufferView : public Object {
+ public:
+  /**
+   * Returns underlying ArrayBuffer.
+   */
+  Local<ArrayBuffer> Buffer();
+  /**
+   * Byte offset in |Buffer|.
+   */
+  size_t ByteOffset();
+  /**
+   * Size of a view in bytes.
+   */
+  size_t ByteLength();
+
+  /**
+   * Copy the contents of the ArrayBufferView's buffer to an embedder defined
+   * memory without additional overhead that calling ArrayBufferView::Buffer
+   * might incur.
+   *
+   * Will write at most min(|byte_length|, ByteLength) bytes starting at
+   * ByteOffset of the underling buffer to the memory starting at |dest|.
+   * Returns the number of bytes actually written.
+   */
+  size_t CopyContents(void* dest, size_t byte_length);
+
+  /**
+   * Returns true if ArrayBufferView's backing ArrayBuffer has already been
+   * allocated.
+   */
+  bool HasBuffer() const;
+
+  V8_INLINE static ArrayBufferView* Cast(Value* obj);
+
+  static const int kInternalFieldCount =
+      V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT;
+
+ private:
+  ArrayBufferView();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * A base class for an instance of TypedArray series of constructors
+ * (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT TypedArray : public ArrayBufferView {
+ public:
+  /**
+   * Number of elements in this typed array
+   * (e.g. for Int16Array, |ByteLength|/2).
+   */
+  size_t Length();
+
+  V8_INLINE static TypedArray* Cast(Value* obj);
+
+ private:
+  TypedArray();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint8Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint8Array : public TypedArray {
+ public:
+  static Local<Uint8Array> New(Local<ArrayBuffer> array_buffer,
+                               size_t byte_offset, size_t length);
+  static Local<Uint8Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                               size_t byte_offset, size_t length);
+  V8_INLINE static Uint8Array* Cast(Value* obj);
+
+ private:
+  Uint8Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint8ClampedArray constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint8ClampedArray : public TypedArray {
+ public:
+  static Local<Uint8ClampedArray> New(Local<ArrayBuffer> array_buffer,
+                                      size_t byte_offset, size_t length);
+  static Local<Uint8ClampedArray> New(
+      Local<SharedArrayBuffer> shared_array_buffer, size_t byte_offset,
+      size_t length);
+  V8_INLINE static Uint8ClampedArray* Cast(Value* obj);
+
+ private:
+  Uint8ClampedArray();
+  static void CheckCast(Value* obj);
+};
+
+/**
+ * An instance of Int8Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Int8Array : public TypedArray {
+ public:
+  static Local<Int8Array> New(Local<ArrayBuffer> array_buffer,
+                              size_t byte_offset, size_t length);
+  static Local<Int8Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                              size_t byte_offset, size_t length);
+  V8_INLINE static Int8Array* Cast(Value* obj);
+
+ private:
+  Int8Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint16Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint16Array : public TypedArray {
+ public:
+  static Local<Uint16Array> New(Local<ArrayBuffer> array_buffer,
+                                size_t byte_offset, size_t length);
+  static Local<Uint16Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                                size_t byte_offset, size_t length);
+  V8_INLINE static Uint16Array* Cast(Value* obj);
+
+ private:
+  Uint16Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Int16Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Int16Array : public TypedArray {
+ public:
+  static Local<Int16Array> New(Local<ArrayBuffer> array_buffer,
+                               size_t byte_offset, size_t length);
+  static Local<Int16Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                               size_t byte_offset, size_t length);
+  V8_INLINE static Int16Array* Cast(Value* obj);
+
+ private:
+  Int16Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint32Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint32Array : public TypedArray {
+ public:
+  static Local<Uint32Array> New(Local<ArrayBuffer> array_buffer,
+                                size_t byte_offset, size_t length);
+  static Local<Uint32Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                                size_t byte_offset, size_t length);
+  V8_INLINE static Uint32Array* Cast(Value* obj);
+
+ private:
+  Uint32Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Int32Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Int32Array : public TypedArray {
+ public:
+  static Local<Int32Array> New(Local<ArrayBuffer> array_buffer,
+                               size_t byte_offset, size_t length);
+  static Local<Int32Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                               size_t byte_offset, size_t length);
+  V8_INLINE static Int32Array* Cast(Value* obj);
+
+ private:
+  Int32Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Float32Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Float32Array : public TypedArray {
+ public:
+  static Local<Float32Array> New(Local<ArrayBuffer> array_buffer,
+                                 size_t byte_offset, size_t length);
+  static Local<Float32Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                                 size_t byte_offset, size_t length);
+  V8_INLINE static Float32Array* Cast(Value* obj);
+
+ private:
+  Float32Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Float64Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Float64Array : public TypedArray {
+ public:
+  static Local<Float64Array> New(Local<ArrayBuffer> array_buffer,
+                                 size_t byte_offset, size_t length);
+  static Local<Float64Array> New(Local<SharedArrayBuffer> shared_array_buffer,
+                                 size_t byte_offset, size_t length);
+  V8_INLINE static Float64Array* Cast(Value* obj);
+
+ private:
+  Float64Array();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of DataView constructor (ES6 draft 15.13.7).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT DataView : public ArrayBufferView {
+ public:
+  static Local<DataView> New(Local<ArrayBuffer> array_buffer,
+                             size_t byte_offset, size_t length);
+  static Local<DataView> New(Local<SharedArrayBuffer> shared_array_buffer,
+                             size_t byte_offset, size_t length);
+  V8_INLINE static DataView* Cast(Value* obj);
+
+ private:
+  DataView();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in SharedArrayBuffer constructor.
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT SharedArrayBuffer : public Object {
+ public:
+  /**
+   * The contents of an |SharedArrayBuffer|. Externalization of
+   * |SharedArrayBuffer| returns an instance of this class, populated, with a
+   * pointer to data and byte length.
+   *
+   * The Data pointer of SharedArrayBuffer::Contents is always allocated with
+   * |ArrayBuffer::Allocator::Allocate| by the allocator specified in
+   * v8::Isolate::CreateParams::array_buffer_allocator.
+   *
+   * This API is experimental and may change significantly.
+   */
+  class V8_EXPORT Contents {  // NOLINT
+   public:
+    Contents() : data_(NULL), byte_length_(0) {}
+
+    void* Data() const { return data_; }
+    size_t ByteLength() const { return byte_length_; }
+
+   private:
+    void* data_;
+    size_t byte_length_;
+
+    friend class SharedArrayBuffer;
+  };
+
+
+  /**
+   * Data length in bytes.
+   */
+  size_t ByteLength() const;
+
+  /**
+   * Create a new SharedArrayBuffer. Allocate |byte_length| bytes.
+   * Allocated memory will be owned by a created SharedArrayBuffer and
+   * will be deallocated when it is garbage-collected,
+   * unless the object is externalized.
+   */
+  static Local<SharedArrayBuffer> New(Isolate* isolate, size_t byte_length);
+
+  /**
+   * Create a new SharedArrayBuffer over an existing memory block.  The created
+   * array buffer is immediately in externalized state unless otherwise
+   * specified. The memory block will not be reclaimed when a created
+   * SharedArrayBuffer is garbage-collected.
+   */
+  static Local<SharedArrayBuffer> New(
+      Isolate* isolate, void* data, size_t byte_length,
+      ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized);
+
+  /**
+   * Returns true if SharedArrayBuffer is externalized, that is, does not
+   * own its memory block.
+   */
+  bool IsExternal() const;
+
+  /**
+   * Make this SharedArrayBuffer external. The pointer to underlying memory
+   * block and byte length are returned as |Contents| structure. After
+   * SharedArrayBuffer had been etxrenalized, it does no longer owns the memory
+   * block. The caller should take steps to free memory when it is no longer
+   * needed.
+   *
+   * The memory block is guaranteed to be allocated with |Allocator::Allocate|
+   * by the allocator specified in
+   * v8::Isolate::CreateParams::array_buffer_allocator.
+   *
+   */
+  Contents Externalize();
+
+  /**
+   * Get a pointer to the ArrayBuffer's underlying memory block without
+   * externalizing it. If the ArrayBuffer is not externalized, this pointer
+   * will become invalid as soon as the ArrayBuffer became garbage collected.
+   *
+   * The embedder should make sure to hold a strong reference to the
+   * ArrayBuffer while accessing this pointer.
+   *
+   * The memory block is guaranteed to be allocated with |Allocator::Allocate|
+   * by the allocator specified in
+   * v8::Isolate::CreateParams::array_buffer_allocator.
+   */
+  Contents GetContents();
+
+  V8_INLINE static SharedArrayBuffer* Cast(Value* obj);
+
+  static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
+
+ private:
+  SharedArrayBuffer();
+  static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in Date constructor (ECMA-262, 15.9).
+ */
+class V8_EXPORT Date : public Object {
+ public:
+  static V8_DEPRECATE_SOON("Use maybe version.",
+                           Local<Value> New(Isolate* isolate, double time));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<Value> New(Local<Context> context,
+                                                     double time);
+
+  /**
+   * A specialization of Value::NumberValue that is more efficient
+   * because we know the structure of this object.
+   */
+  double ValueOf() const;
+
+  V8_INLINE static Date* Cast(v8::Value* obj);
+
+  /**
+   * Notification that the embedder has changed the time zone,
+   * daylight savings time, or other date / time configuration
+   * parameters.  V8 keeps a cache of various values used for
+   * date / time computation.  This notification will reset
+   * those cached values for the current context so that date /
+   * time configuration changes would be reflected in the Date
+   * object.
+   *
+   * This API should not be called more than needed as it will
+   * negatively impact the performance of date operations.
+   */
+  static void DateTimeConfigurationChangeNotification(Isolate* isolate);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A Number object (ECMA-262, 4.3.21).
+ */
+class V8_EXPORT NumberObject : public Object {
+ public:
+  static Local<Value> New(Isolate* isolate, double value);
+
+  double ValueOf() const;
+
+  V8_INLINE static NumberObject* Cast(v8::Value* obj);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A Boolean object (ECMA-262, 4.3.15).
+ */
+class V8_EXPORT BooleanObject : public Object {
+ public:
+  static Local<Value> New(bool value);
+
+  bool ValueOf() const;
+
+  V8_INLINE static BooleanObject* Cast(v8::Value* obj);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A String object (ECMA-262, 4.3.18).
+ */
+class V8_EXPORT StringObject : public Object {
+ public:
+  static Local<Value> New(Local<String> value);
+
+  Local<String> ValueOf() const;
+
+  V8_INLINE static StringObject* Cast(v8::Value* obj);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A Symbol object (ECMA-262 edition 6).
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8_EXPORT SymbolObject : public Object {
+ public:
+  static Local<Value> New(Isolate* isolate, Local<Symbol> value);
+
+  Local<Symbol> ValueOf() const;
+
+  V8_INLINE static SymbolObject* Cast(v8::Value* obj);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * An instance of the built-in RegExp constructor (ECMA-262, 15.10).
+ */
+class V8_EXPORT RegExp : public Object {
+ public:
+  /**
+   * Regular expression flag bits. They can be or'ed to enable a set
+   * of flags.
+   */
+  enum Flags {
+    kNone = 0,
+    kGlobal = 1,
+    kIgnoreCase = 2,
+    kMultiline = 4
+  };
+
+  /**
+   * Creates a regular expression from the given pattern string and
+   * the flags bit field. May throw a JavaScript exception as
+   * described in ECMA-262, 15.10.4.1.
+   *
+   * For example,
+   *   RegExp::New(v8::String::New("foo"),
+   *               static_cast<RegExp::Flags>(kGlobal | kMultiline))
+   * is equivalent to evaluating "/foo/gm".
+   */
+  static V8_DEPRECATE_SOON("Use maybe version",
+                           Local<RegExp> New(Local<String> pattern,
+                                             Flags flags));
+  static V8_WARN_UNUSED_RESULT MaybeLocal<RegExp> New(Local<Context> context,
+                                                      Local<String> pattern,
+                                                      Flags flags);
+
+  /**
+   * Returns the value of the source property: a string representing
+   * the regular expression.
+   */
+  Local<String> GetSource() const;
+
+  /**
+   * Returns the flags bit field.
+   */
+  Flags GetFlags() const;
+
+  V8_INLINE static RegExp* Cast(v8::Value* obj);
+
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value that wraps a C++ void*. This type of value is mainly used
+ * to associate C++ data structures with JavaScript objects.
+ */
+class V8_EXPORT External : public Value {
+ public:
+  static Local<External> New(Isolate* isolate, void* value);
+  V8_INLINE static External* Cast(Value* obj);
+  void* Value() const;
+ private:
+  static void CheckCast(v8::Value* obj);
+};
+
+
+#define V8_INTRINSICS_LIST(F) F(ArrayProto_values, array_values_iterator)
+
+enum Intrinsic {
+#define V8_DECL_INTRINSIC(name, iname) k##name,
+  V8_INTRINSICS_LIST(V8_DECL_INTRINSIC)
+#undef V8_DECL_INTRINSIC
+};
+
+
+// --- Templates ---
+
+
+/**
+ * The superclass of object and function templates.
+ */
+class V8_EXPORT Template : public Data {
+ public:
+  /** Adds a property to each instance created by this template.*/
+  void Set(Local<Name> name, Local<Data> value,
+           PropertyAttribute attributes = None);
+  V8_INLINE void Set(Isolate* isolate, const char* name, Local<Data> value);
+
+  void SetAccessorProperty(
+     Local<Name> name,
+     Local<FunctionTemplate> getter = Local<FunctionTemplate>(),
+     Local<FunctionTemplate> setter = Local<FunctionTemplate>(),
+     PropertyAttribute attribute = None,
+     AccessControl settings = DEFAULT);
+
+#ifdef V8_JS_ACCESSORS
+  void SetAccessorProperty(Local<Name> name,
+                           Local<Function> getter = Local<Function>(),
+                           Local<Function> setter = Local<Function>(),
+                           PropertyAttribute attribute = None);
+#endif  // V8_JS_ACCESSORS
+
+  /**
+   * Whenever the property with the given name is accessed on objects
+   * created from this Template the getter and setter callbacks
+   * are called instead of getting and setting the property directly
+   * on the JavaScript object.
+   *
+   * \param name The name of the property for which an accessor is added.
+   * \param getter The callback to invoke when getting the property.
+   * \param setter The callback to invoke when setting the property.
+   * \param data A piece of data that will be passed to the getter and setter
+   *   callbacks whenever they are invoked.
+   * \param settings Access control settings for the accessor. This is a bit
+   *   field consisting of one of more of
+   *   DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2.
+   *   The default is to not allow cross-context access.
+   *   ALL_CAN_READ means that all cross-context reads are allowed.
+   *   ALL_CAN_WRITE means that all cross-context writes are allowed.
+   *   The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all
+   *   cross-context access.
+   * \param attribute The attributes of the property for which an accessor
+   *   is added.
+   * \param signature The signature describes valid receivers for the accessor
+   *   and is used to perform implicit instance checks against them. If the
+   *   receiver is incompatible (i.e. is not an instance of the constructor as
+   *   defined by FunctionTemplate::HasInstance()), an implicit TypeError is
+   *   thrown and no callback is invoked.
+   */
+  void SetNativeDataProperty(
+      Local<String> name, AccessorGetterCallback getter,
+      AccessorSetterCallback setter = 0,
+      // TODO(dcarney): gcc can't handle Local below
+      Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
+      Local<AccessorSignature> signature = Local<AccessorSignature>(),
+      AccessControl settings = DEFAULT);
+  void SetNativeDataProperty(
+      Local<Name> name, AccessorNameGetterCallback getter,
+      AccessorNameSetterCallback setter = 0,
+      // TODO(dcarney): gcc can't handle Local below
+      Local<Value> data = Local<Value>(), PropertyAttribute attribute = None,
+      Local<AccessorSignature> signature = Local<AccessorSignature>(),
+      AccessControl settings = DEFAULT);
+
+  /**
+   * During template instantiation, sets the value with the intrinsic property
+   * from the correct context.
+   */
+  void SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic,
+                                PropertyAttribute attribute = None);
+
+ private:
+  Template();
+
+  friend class ObjectTemplate;
+  friend class FunctionTemplate;
+};
+
+
+/**
+ * NamedProperty[Getter|Setter] are used as interceptors on object.
+ * See ObjectTemplate::SetNamedPropertyHandler.
+ */
+typedef void (*NamedPropertyGetterCallback)(
+    Local<String> property,
+    const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef void (*NamedPropertySetterCallback)(
+    Local<String> property,
+    Local<Value> value,
+    const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes (like v8::None,
+ * v8::DontEnum, etc.)
+ */
+typedef void (*NamedPropertyQueryCallback)(
+    Local<String> property,
+    const PropertyCallbackInfo<Integer>& info);
+
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef void (*NamedPropertyDeleterCallback)(
+    Local<String> property,
+    const PropertyCallbackInfo<Boolean>& info);
+
+
+/**
+ * Returns an array containing the names of the properties the named
+ * property getter intercepts.
+ */
+typedef void (*NamedPropertyEnumeratorCallback)(
+    const PropertyCallbackInfo<Array>& info);
+
+
+// TODO(dcarney): Deprecate and remove previous typedefs, and replace
+// GenericNamedPropertyFooCallback with just NamedPropertyFooCallback.
+/**
+ * GenericNamedProperty[Getter|Setter] are used as interceptors on object.
+ * See ObjectTemplate::SetNamedPropertyHandler.
+ */
+typedef void (*GenericNamedPropertyGetterCallback)(
+    Local<Name> property, const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef void (*GenericNamedPropertySetterCallback)(
+    Local<Name> property, Local<Value> value,
+    const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes (like v8::None,
+ * v8::DontEnum, etc.)
+ */
+typedef void (*GenericNamedPropertyQueryCallback)(
+    Local<Name> property, const PropertyCallbackInfo<Integer>& info);
+
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef void (*GenericNamedPropertyDeleterCallback)(
+    Local<Name> property, const PropertyCallbackInfo<Boolean>& info);
+
+
+/**
+ * Returns an array containing the names of the properties the named
+ * property getter intercepts.
+ */
+typedef void (*GenericNamedPropertyEnumeratorCallback)(
+    const PropertyCallbackInfo<Array>& info);
+
+
+/**
+ * Returns the value of the property if the getter intercepts the
+ * request.  Otherwise, returns an empty handle.
+ */
+typedef void (*IndexedPropertyGetterCallback)(
+    uint32_t index,
+    const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef void (*IndexedPropertySetterCallback)(
+    uint32_t index,
+    Local<Value> value,
+    const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes.
+ */
+typedef void (*IndexedPropertyQueryCallback)(
+    uint32_t index,
+    const PropertyCallbackInfo<Integer>& info);
+
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef void (*IndexedPropertyDeleterCallback)(
+    uint32_t index,
+    const PropertyCallbackInfo<Boolean>& info);
+
+
+/**
+ * Returns an array containing the indices of the properties the
+ * indexed property getter intercepts.
+ */
+typedef void (*IndexedPropertyEnumeratorCallback)(
+    const PropertyCallbackInfo<Array>& info);
+
+
+/**
+ * Access type specification.
+ */
+enum AccessType {
+  ACCESS_GET,
+  ACCESS_SET,
+  ACCESS_HAS,
+  ACCESS_DELETE,
+  ACCESS_KEYS
+};
+
+
+/**
+ * Returns true if the given context should be allowed to access the given
+ * object.
+ */
+typedef bool (*AccessCheckCallback)(Local<Context> accessing_context,
+                                    Local<Object> accessed_object);
+
+
+/**
+ * Returns true if cross-context access should be allowed to the named
+ * property with the given key on the host object.
+ */
+typedef bool (*NamedSecurityCallback)(Local<Object> host,
+                                      Local<Value> key,
+                                      AccessType type,
+                                      Local<Value> data);
+
+
+/**
+ * Returns true if cross-context access should be allowed to the indexed
+ * property with the given index on the host object.
+ */
+typedef bool (*IndexedSecurityCallback)(Local<Object> host,
+                                        uint32_t index,
+                                        AccessType type,
+                                        Local<Value> data);
+
+
+/**
+ * A FunctionTemplate is used to create functions at runtime. There
+ * can only be one function created from a FunctionTemplate in a
+ * context.  The lifetime of the created function is equal to the
+ * lifetime of the context.  So in case the embedder needs to create
+ * temporary functions that can be collected using Scripts is
+ * preferred.
+ *
+ * Any modification of a FunctionTemplate after first instantiation will trigger
+ *a crash.
+ *
+ * A FunctionTemplate can have properties, these properties are added to the
+ * function object when it is created.
+ *
+ * A FunctionTemplate has a corresponding instance template which is
+ * used to create object instances when the function is used as a
+ * constructor. Properties added to the instance template are added to
+ * each object instance.
+ *
+ * A FunctionTemplate can have a prototype template. The prototype template
+ * is used to create the prototype object of the function.
+ *
+ * The following example shows how to use a FunctionTemplate:
+ *
+ * \code
+ *    v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
+ *    t->Set("func_property", v8::Number::New(1));
+ *
+ *    v8::Local<v8::Template> proto_t = t->PrototypeTemplate();
+ *    proto_t->Set("proto_method", v8::FunctionTemplate::New(InvokeCallback));
+ *    proto_t->Set("proto_const", v8::Number::New(2));
+ *
+ *    v8::Local<v8::ObjectTemplate> instance_t = t->InstanceTemplate();
+ *    instance_t->SetAccessor("instance_accessor", InstanceAccessorCallback);
+ *    instance_t->SetNamedPropertyHandler(PropertyHandlerCallback, ...);
+ *    instance_t->Set("instance_property", Number::New(3));
+ *
+ *    v8::Local<v8::Function> function = t->GetFunction();
+ *    v8::Local<v8::Object> instance = function->NewInstance();
+ * \endcode
+ *
+ * Let's use "function" as the JS variable name of the function object
+ * and "instance" for the instance object created above.  The function
+ * and the instance will have the following properties:
+ *
+ * \code
+ *   func_property in function == true;
+ *   function.func_property == 1;
+ *
+ *   function.prototype.proto_method() invokes 'InvokeCallback'
+ *   function.prototype.proto_const == 2;
+ *
+ *   instance instanceof function == true;
+ *   instance.instance_accessor calls 'InstanceAccessorCallback'
+ *   instance.instance_property == 3;
+ * \endcode
+ *
+ * A FunctionTemplate can inherit from another one by calling the
+ * FunctionTemplate::Inherit method.  The following graph illustrates
+ * the semantics of inheritance:
+ *
+ * \code
+ *   FunctionTemplate Parent  -> Parent() . prototype -> { }
+ *     ^                                                  ^
+ *     | Inherit(Parent)                                  | .__proto__
+ *     |                                                  |
+ *   FunctionTemplate Child   -> Child()  . prototype -> { }
+ * \endcode
+ *
+ * A FunctionTemplate 'Child' inherits from 'Parent', the prototype
+ * object of the Child() function has __proto__ pointing to the
+ * Parent() function's prototype object. An instance of the Child
+ * function has all properties on Parent's instance templates.
+ *
+ * Let Parent be the FunctionTemplate initialized in the previous
+ * section and create a Child FunctionTemplate by:
+ *
+ * \code
+ *   Local<FunctionTemplate> parent = t;
+ *   Local<FunctionTemplate> child = FunctionTemplate::New();
+ *   child->Inherit(parent);
+ *
+ *   Local<Function> child_function = child->GetFunction();
+ *   Local<Object> child_instance = child_function->NewInstance();
+ * \endcode
+ *
+ * The Child function and Child instance will have the following
+ * properties:
+ *
+ * \code
+ *   child_func.prototype.__proto__ == function.prototype;
+ *   child_instance.instance_accessor calls 'InstanceAccessorCallback'
+ *   child_instance.instance_property == 3;
+ * \endcode
+ */
+class V8_EXPORT FunctionTemplate : public Template {
+ public:
+  /** Creates a function template.*/
+  static Local<FunctionTemplate> New(
+      Isolate* isolate, FunctionCallback callback = 0,
+      Local<Value> data = Local<Value>(),
+      Local<Signature> signature = Local<Signature>(), int length = 0);
+
+  /** Returns the unique function instance in the current execution context.*/
+  V8_DEPRECATE_SOON("Use maybe version", Local<Function> GetFunction());
+  V8_WARN_UNUSED_RESULT MaybeLocal<Function> GetFunction(
+      Local<Context> context);
+
+  /**
+   * Set the call-handler callback for a FunctionTemplate.  This
+   * callback is called whenever the function created from this
+   * FunctionTemplate is called.
+   */
+  void SetCallHandler(FunctionCallback callback,
+                      Local<Value> data = Local<Value>());
+
+  /** Set the predefined length property for the FunctionTemplate. */
+  void SetLength(int length);
+
+  /** Get the InstanceTemplate. */
+  Local<ObjectTemplate> InstanceTemplate();
+
+  /** Causes the function template to inherit from a parent function template.*/
+  void Inherit(Local<FunctionTemplate> parent);
+
+  /**
+   * A PrototypeTemplate is the template used to create the prototype object
+   * of the function created by this template.
+   */
+  Local<ObjectTemplate> PrototypeTemplate();
+
+  /**
+   * Set the class name of the FunctionTemplate.  This is used for
+   * printing objects created with the function created from the
+   * FunctionTemplate as its constructor.
+   */
+  void SetClassName(Local<String> name);
+
+
+  /**
+   * When set to true, no access check will be performed on the receiver of a
+   * function call.  Currently defaults to true, but this is subject to change.
+   */
+  void SetAcceptAnyReceiver(bool value);
+
+  /**
+   * Determines whether the __proto__ accessor ignores instances of
+   * the function template.  If instances of the function template are
+   * ignored, __proto__ skips all instances and instead returns the
+   * next object in the prototype chain.
+   *
+   * Call with a value of true to make the __proto__ accessor ignore
+   * instances of the function template.  Call with a value of false
+   * to make the __proto__ accessor not ignore instances of the
+   * function template.  By default, instances of a function template
+   * are not ignored.
+   */
+  void SetHiddenPrototype(bool value);
+
+  /**
+   * Sets the ReadOnly flag in the attributes of the 'prototype' property
+   * of functions created from this FunctionTemplate to true.
+   */
+  void ReadOnlyPrototype();
+
+  /**
+   * Removes the prototype property from functions created from this
+   * FunctionTemplate.
+   */
+  void RemovePrototype();
+
+  /**
+   * Returns true if the given object is an instance of this function
+   * template.
+   */
+  bool HasInstance(Local<Value> object);
+
+ private:
+  FunctionTemplate();
+  friend class Context;
+  friend class ObjectTemplate;
+};
+
+
+enum class PropertyHandlerFlags {
+  kNone = 0,
+  // See ALL_CAN_READ above.
+  kAllCanRead = 1,
+  // Will not call into interceptor for properties on the receiver or prototype
+  // chain.  Currently only valid for named interceptors.
+  kNonMasking = 1 << 1,
+  // Will not call into interceptor for symbol lookup.  Only meaningful for
+  // named interceptors.
+  kOnlyInterceptStrings = 1 << 2,
+};
+
+
+struct NamedPropertyHandlerConfiguration {
+  NamedPropertyHandlerConfiguration(
+      /** Note: getter is required **/
+      GenericNamedPropertyGetterCallback getter = 0,
+      GenericNamedPropertySetterCallback setter = 0,
+      GenericNamedPropertyQueryCallback query = 0,
+      GenericNamedPropertyDeleterCallback deleter = 0,
+      GenericNamedPropertyEnumeratorCallback enumerator = 0,
+      Local<Value> data = Local<Value>(),
+      PropertyHandlerFlags flags = PropertyHandlerFlags::kNone)
+      : getter(getter),
+        setter(setter),
+        query(query),
+        deleter(deleter),
+        enumerator(enumerator),
+        data(data),
+        flags(flags) {}
+
+  GenericNamedPropertyGetterCallback getter;
+  GenericNamedPropertySetterCallback setter;
+  GenericNamedPropertyQueryCallback query;
+  GenericNamedPropertyDeleterCallback deleter;
+  GenericNamedPropertyEnumeratorCallback enumerator;
+  Local<Value> data;
+  PropertyHandlerFlags flags;
+};
+
+
+struct IndexedPropertyHandlerConfiguration {
+  IndexedPropertyHandlerConfiguration(
+      /** Note: getter is required **/
+      IndexedPropertyGetterCallback getter = 0,
+      IndexedPropertySetterCallback setter = 0,
+      IndexedPropertyQueryCallback query = 0,
+      IndexedPropertyDeleterCallback deleter = 0,
+      IndexedPropertyEnumeratorCallback enumerator = 0,
+      Local<Value> data = Local<Value>(),
+      PropertyHandlerFlags flags = PropertyHandlerFlags::kNone)
+      : getter(getter),
+        setter(setter),
+        query(query),
+        deleter(deleter),
+        enumerator(enumerator),
+        data(data),
+        flags(flags) {}
+
+  IndexedPropertyGetterCallback getter;
+  IndexedPropertySetterCallback setter;
+  IndexedPropertyQueryCallback query;
+  IndexedPropertyDeleterCallback deleter;
+  IndexedPropertyEnumeratorCallback enumerator;
+  Local<Value> data;
+  PropertyHandlerFlags flags;
+};
+
+
+/**
+ * An ObjectTemplate is used to create objects at runtime.
+ *
+ * Properties added to an ObjectTemplate are added to each object
+ * created from the ObjectTemplate.
+ */
+class V8_EXPORT ObjectTemplate : public Template {
+ public:
+  /** Creates an ObjectTemplate. */
+  static Local<ObjectTemplate> New(
+      Isolate* isolate,
+      Local<FunctionTemplate> constructor = Local<FunctionTemplate>());
+  static V8_DEPRECATE_SOON("Use isolate version", Local<ObjectTemplate> New());
+
+  /** Creates a new instance of this template.*/
+  V8_DEPRECATE_SOON("Use maybe version", Local<Object> NewInstance());
+  V8_WARN_UNUSED_RESULT MaybeLocal<Object> NewInstance(Local<Context> context);
+
+  /**
+   * Sets an accessor on the object template.
+   *
+   * Whenever the property with the given name is accessed on objects
+   * created from this ObjectTemplate the getter and setter callbacks
+   * are called instead of getting and setting the property directly
+   * on the JavaScript object.
+   *
+   * \param name The name of the property for which an accessor is added.
+   * \param getter The callback to invoke when getting the property.
+   * \param setter The callback to invoke when setting the property.
+   * \param data A piece of data that will be passed to the getter and setter
+   *   callbacks whenever they are invoked.
+   * \param settings Access control settings for the accessor. This is a bit
+   *   field consisting of one of more of
+   *   DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2.
+   *   The default is to not allow cross-context access.
+   *   ALL_CAN_READ means that all cross-context reads are allowed.
+   *   ALL_CAN_WRITE means that all cross-context writes are allowed.
+   *   The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all
+   *   cross-context access.
+   * \param attribute The attributes of the property for which an accessor
+   *   is added.
+   * \param signature The signature describes valid receivers for the accessor
+   *   and is used to perform implicit instance checks against them. If the
+   *   receiver is incompatible (i.e. is not an instance of the constructor as
+   *   defined by FunctionTemplate::HasInstance()), an implicit TypeError is
+   *   thrown and no callback is invoked.
+   */
+  void SetAccessor(
+      Local<String> name, AccessorGetterCallback getter,
+      AccessorSetterCallback setter = 0, Local<Value> data = Local<Value>(),
+      AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
+      Local<AccessorSignature> signature = Local<AccessorSignature>());
+  void SetAccessor(
+      Local<Name> name, AccessorNameGetterCallback getter,
+      AccessorNameSetterCallback setter = 0, Local<Value> data = Local<Value>(),
+      AccessControl settings = DEFAULT, PropertyAttribute attribute = None,
+      Local<AccessorSignature> signature = Local<AccessorSignature>());
+
+  /**
+   * Sets a named property handler on the object template.
+   *
+   * Whenever a property whose name is a string is accessed on objects created
+   * from this object template, the provided callback is invoked instead of
+   * accessing the property directly on the JavaScript object.
+   *
+   * Note that new code should use the second version that can intercept
+   * symbol-named properties as well as string-named properties.
+   *
+   * \param getter The callback to invoke when getting a property.
+   * \param setter The callback to invoke when setting a property.
+   * \param query The callback to invoke to check if a property is present,
+   *   and if present, get its attributes.
+   * \param deleter The callback to invoke when deleting a property.
+   * \param enumerator The callback to invoke to enumerate all the named
+   *   properties of an object.
+   * \param data A piece of data that will be passed to the callbacks
+   *   whenever they are invoked.
+   */
+  // TODO(dcarney): deprecate
+  void SetNamedPropertyHandler(NamedPropertyGetterCallback getter,
+                               NamedPropertySetterCallback setter = 0,
+                               NamedPropertyQueryCallback query = 0,
+                               NamedPropertyDeleterCallback deleter = 0,
+                               NamedPropertyEnumeratorCallback enumerator = 0,
+                               Local<Value> data = Local<Value>());
+  void SetHandler(const NamedPropertyHandlerConfiguration& configuration);
+
+  /**
+   * Sets an indexed property handler on the object template.
+   *
+   * Whenever an indexed property is accessed on objects created from
+   * this object template, the provided callback is invoked instead of
+   * accessing the property directly on the JavaScript object.
+   *
+   * \param getter The callback to invoke when getting a property.
+   * \param setter The callback to invoke when setting a property.
+   * \param query The callback to invoke to check if an object has a property.
+   * \param deleter The callback to invoke when deleting a property.
+   * \param enumerator The callback to invoke to enumerate all the indexed
+   *   properties of an object.
+   * \param data A piece of data that will be passed to the callbacks
+   *   whenever they are invoked.
+   */
+  void SetHandler(const IndexedPropertyHandlerConfiguration& configuration);
+  // TODO(dcarney): deprecate
+  void SetIndexedPropertyHandler(
+      IndexedPropertyGetterCallback getter,
+      IndexedPropertySetterCallback setter = 0,
+      IndexedPropertyQueryCallback query = 0,
+      IndexedPropertyDeleterCallback deleter = 0,
+      IndexedPropertyEnumeratorCallback enumerator = 0,
+      Local<Value> data = Local<Value>()) {
+    SetHandler(IndexedPropertyHandlerConfiguration(getter, setter, query,
+                                                   deleter, enumerator, data));
+  }
+  /**
+   * Sets the callback to be used when calling instances created from
+   * this template as a function.  If no callback is set, instances
+   * behave like normal JavaScript objects that cannot be called as a
+   * function.
+   */
+  void SetCallAsFunctionHandler(FunctionCallback callback,
+                                Local<Value> data = Local<Value>());
+
+  /**
+   * Mark object instances of the template as undetectable.
+   *
+   * In many ways, undetectable objects behave as though they are not
+   * there.  They behave like 'undefined' in conditionals and when
+   * printed.  However, properties can be accessed and called as on
+   * normal objects.
+   */
+  void MarkAsUndetectable();
+
+  /**
+   * Sets access check callback on the object template and enables access
+   * checks.
+   *
+   * When accessing properties on instances of this object template,
+   * the access check callback will be called to determine whether or
+   * not to allow cross-context access to the properties.
+   */
+  void SetAccessCheckCallback(AccessCheckCallback callback,
+                              Local<Value> data = Local<Value>());
+
+  V8_DEPRECATE_SOON(
+      "Use SetAccessCheckCallback instead",
+      void SetAccessCheckCallbacks(NamedSecurityCallback named_handler,
+                                   IndexedSecurityCallback indexed_handler,
+                                   Local<Value> data = Local<Value>()));
+
+  /**
+   * Gets the number of internal fields for objects generated from
+   * this template.
+   */
+  int InternalFieldCount();
+
+  /**
+   * Sets the number of internal fields for objects generated from
+   * this template.
+   */
+  void SetInternalFieldCount(int value);
+
+ private:
+  ObjectTemplate();
+  static Local<ObjectTemplate> New(internal::Isolate* isolate,
+                                   Local<FunctionTemplate> constructor);
+  friend class FunctionTemplate;
+};
+
+
+/**
+ * A Signature specifies which receiver is valid for a function.
+ */
+class V8_EXPORT Signature : public Data {
+ public:
+  static Local<Signature> New(
+      Isolate* isolate,
+      Local<FunctionTemplate> receiver = Local<FunctionTemplate>());
+
+ private:
+  Signature();
+};
+
+
+/**
+ * An AccessorSignature specifies which receivers are valid parameters
+ * to an accessor callback.
+ */
+class V8_EXPORT AccessorSignature : public Data {
+ public:
+  static Local<AccessorSignature> New(
+      Isolate* isolate,
+      Local<FunctionTemplate> receiver = Local<FunctionTemplate>());
+
+ private:
+  AccessorSignature();
+};
+
+
+/**
+ * A utility for determining the type of objects based on the template
+ * they were constructed from.
+ */
+class V8_EXPORT TypeSwitch : public Data {
+ public:
+  static Local<TypeSwitch> New(Local<FunctionTemplate> type);
+  static Local<TypeSwitch> New(int argc, Local<FunctionTemplate> types[]);
+  int match(Local<Value> value);
+
+ private:
+  TypeSwitch();
+};
+
+
+// --- Extensions ---
+
+class V8_EXPORT ExternalOneByteStringResourceImpl
+    : public String::ExternalOneByteStringResource {
+ public:
+  ExternalOneByteStringResourceImpl() : data_(0), length_(0) {}
+  ExternalOneByteStringResourceImpl(const char* data, size_t length)
+      : data_(data), length_(length) {}
+  const char* data() const { return data_; }
+  size_t length() const { return length_; }
+
+ private:
+  const char* data_;
+  size_t length_;
+};
+
+/**
+ * Ignore
+ */
+class V8_EXPORT Extension {  // NOLINT
+ public:
+  // Note that the strings passed into this constructor must live as long
+  // as the Extension itself.
+  Extension(const char* name,
+            const char* source = 0,
+            int dep_count = 0,
+            const char** deps = 0,
+            int source_length = -1);
+  virtual ~Extension() { }
+  virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
+      v8::Isolate* isolate, v8::Local<v8::String> name) {
+    return v8::Local<v8::FunctionTemplate>();
+  }
+
+  const char* name() const { return name_; }
+  size_t source_length() const { return source_length_; }
+  const String::ExternalOneByteStringResource* source() const {
+    return &source_; }
+  int dependency_count() { return dep_count_; }
+  const char** dependencies() { return deps_; }
+  void set_auto_enable(bool value) { auto_enable_ = value; }
+  bool auto_enable() { return auto_enable_; }
+
+ private:
+  const char* name_;
+  size_t source_length_;  // expected to initialize before source_
+  ExternalOneByteStringResourceImpl source_;
+  int dep_count_;
+  const char** deps_;
+  bool auto_enable_;
+
+  // Disallow copying and assigning.
+  Extension(const Extension&);
+  void operator=(const Extension&);
+};
+
+
+void V8_EXPORT RegisterExtension(Extension* extension);
+
+
+// --- Statics ---
+
+V8_INLINE Local<Primitive> Undefined(Isolate* isolate);
+V8_INLINE Local<Primitive> Null(Isolate* isolate);
+V8_INLINE Local<Boolean> True(Isolate* isolate);
+V8_INLINE Local<Boolean> False(Isolate* isolate);
+
+
+/**
+ * A set of constraints that specifies the limits of the runtime's memory use.
+ * You must set the heap size before initializing the VM - the size cannot be
+ * adjusted after the VM is initialized.
+ *
+ * If you are using threads then you should hold the V8::Locker lock while
+ * setting the stack limit and you must set a non-default stack limit separately
+ * for each thread.
+ */
+class V8_EXPORT ResourceConstraints {
+ public:
+  ResourceConstraints();
+
+  /**
+   * Configures the constraints with reasonable default values based on the
+   * capabilities of the current device the VM is running on.
+   *
+   * \param physical_memory The total amount of physical memory on the current
+   *   device, in bytes.
+   * \param virtual_memory_limit The amount of virtual memory on the current
+   *   device, in bytes, or zero, if there is no limit.
+   */
+  void ConfigureDefaults(uint64_t physical_memory,
+                         uint64_t virtual_memory_limit);
+
+  int max_semi_space_size() const { return max_semi_space_size_; }
+  void set_max_semi_space_size(int value) { max_semi_space_size_ = value; }
+  int max_old_space_size() const { return max_old_space_size_; }
+  void set_max_old_space_size(int value) { max_old_space_size_ = value; }
+  int max_executable_size() const { return max_executable_size_; }
+  void set_max_executable_size(int value) { max_executable_size_ = value; }
+  uint32_t* stack_limit() const { return stack_limit_; }
+  // Sets an address beyond which the VM's stack may not grow.
+  void set_stack_limit(uint32_t* value) { stack_limit_ = value; }
+  size_t code_range_size() const { return code_range_size_; }
+  void set_code_range_size(size_t value) {
+    code_range_size_ = value;
+  }
+
+ private:
+  int max_semi_space_size_;
+  int max_old_space_size_;
+  int max_executable_size_;
+  uint32_t* stack_limit_;
+  size_t code_range_size_;
+};
+
+
+// --- Exceptions ---
+
+
+typedef void (*FatalErrorCallback)(const char* location, const char* message);
+
+
+typedef void (*MessageCallback)(Local<Message> message, Local<Value> error);
+
+// --- Tracing ---
+
+typedef void (*LogEventCallback)(const char* name, int event);
+
+/**
+ * Create new error objects by calling the corresponding error object
+ * constructor with the message.
+ */
+class V8_EXPORT Exception {
+ public:
+  static Local<Value> RangeError(Local<String> message);
+  static Local<Value> ReferenceError(Local<String> message);
+  static Local<Value> SyntaxError(Local<String> message);
+  static Local<Value> TypeError(Local<String> message);
+  static Local<Value> Error(Local<String> message);
+
+  /**
+   * Creates an error message for the given exception.
+   * Will try to reconstruct the original stack trace from the exception value,
+   * or capture the current stack trace if not available.
+   */
+  static Local<Message> CreateMessage(Local<Value> exception);
+
+  /**
+   * Returns the original stack trace that was captured at the creation time
+   * of a given exception, or an empty handle if not available.
+   */
+  static Local<StackTrace> GetStackTrace(Local<Value> exception);
+};
+
+
+// --- Counters Callbacks ---
+
+typedef int* (*CounterLookupCallback)(const char* name);
+
+typedef void* (*CreateHistogramCallback)(const char* name,
+                                         int min,
+                                         int max,
+                                         size_t buckets);
+
+typedef void (*AddHistogramSampleCallback)(void* histogram, int sample);
+
+// --- Memory Allocation Callback ---
+enum ObjectSpace {
+  kObjectSpaceNewSpace = 1 << 0,
+  kObjectSpaceOldSpace = 1 << 1,
+  kObjectSpaceCodeSpace = 1 << 2,
+  kObjectSpaceMapSpace = 1 << 3,
+  kObjectSpaceLoSpace = 1 << 4,
+  kObjectSpaceAll = kObjectSpaceNewSpace | kObjectSpaceOldSpace |
+                    kObjectSpaceCodeSpace | kObjectSpaceMapSpace |
+                    kObjectSpaceLoSpace
+};
+
+  enum AllocationAction {
+    kAllocationActionAllocate = 1 << 0,
+    kAllocationActionFree = 1 << 1,
+    kAllocationActionAll = kAllocationActionAllocate | kAllocationActionFree
+  };
+
+typedef void (*MemoryAllocationCallback)(ObjectSpace space,
+                                         AllocationAction action,
+                                         int size);
+
+// --- Leave Script Callback ---
+typedef void (*CallCompletedCallback)();
+
+// --- Promise Reject Callback ---
+enum PromiseRejectEvent {
+  kPromiseRejectWithNoHandler = 0,
+  kPromiseHandlerAddedAfterReject = 1
+};
+
+class PromiseRejectMessage {
+ public:
+  PromiseRejectMessage(Local<Promise> promise, PromiseRejectEvent event,
+                       Local<Value> value, Local<StackTrace> stack_trace)
+      : promise_(promise),
+        event_(event),
+        value_(value),
+        stack_trace_(stack_trace) {}
+
+  V8_INLINE Local<Promise> GetPromise() const { return promise_; }
+  V8_INLINE PromiseRejectEvent GetEvent() const { return event_; }
+  V8_INLINE Local<Value> GetValue() const { return value_; }
+
+  // DEPRECATED. Use v8::Exception::CreateMessage(GetValue())->GetStackTrace()
+  V8_INLINE Local<StackTrace> GetStackTrace() const { return stack_trace_; }
+
+ private:
+  Local<Promise> promise_;
+  PromiseRejectEvent event_;
+  Local<Value> value_;
+  Local<StackTrace> stack_trace_;
+};
+
+typedef void (*PromiseRejectCallback)(PromiseRejectMessage message);
+
+// --- Microtask Callback ---
+typedef void (*MicrotaskCallback)(void* data);
+
+// --- Failed Access Check Callback ---
+typedef void (*FailedAccessCheckCallback)(Local<Object> target,
+                                          AccessType type,
+                                          Local<Value> data);
+
+// --- AllowCodeGenerationFromStrings callbacks ---
+
+/**
+ * Callback to check if code generation from strings is allowed. See
+ * Context::AllowCodeGenerationFromStrings.
+ */
+typedef bool (*AllowCodeGenerationFromStringsCallback)(Local<Context> context);
+
+// --- Garbage Collection Callbacks ---
+
+/**
+ * Applications can register callback functions which will be called before and
+ * after certain garbage collection operations.  Allocations are not allowed in
+ * the callback functions, you therefore cannot manipulate objects (set or
+ * delete properties for example) since it is possible such operations will
+ * result in the allocation of objects.
+ */
+enum GCType {
+  kGCTypeScavenge = 1 << 0,
+  kGCTypeMarkSweepCompact = 1 << 1,
+  kGCTypeIncrementalMarking = 1 << 2,
+  kGCTypeProcessWeakCallbacks = 1 << 3,
+  kGCTypeAll = kGCTypeScavenge | kGCTypeMarkSweepCompact |
+               kGCTypeIncrementalMarking | kGCTypeProcessWeakCallbacks
+};
+
+enum GCCallbackFlags {
+  kNoGCCallbackFlags = 0,
+  kGCCallbackFlagConstructRetainedObjectInfos = 1 << 1,
+  kGCCallbackFlagForced = 1 << 2,
+  kGCCallbackFlagSynchronousPhantomCallbackProcessing = 1 << 3
+};
+
+V8_DEPRECATE_SOON("Use GCCallBack instead",
+                  typedef void (*GCPrologueCallback)(GCType type,
+                                                     GCCallbackFlags flags));
+V8_DEPRECATE_SOON("Use GCCallBack instead",
+                  typedef void (*GCEpilogueCallback)(GCType type,
+                                                     GCCallbackFlags flags));
+typedef void (*GCCallback)(GCType type, GCCallbackFlags flags);
+
+typedef void (*InterruptCallback)(Isolate* isolate, void* data);
+
+
+/**
+ * Collection of V8 heap information.
+ *
+ * Instances of this class can be passed to v8::V8::HeapStatistics to
+ * get heap statistics from V8.
+ */
+class V8_EXPORT HeapStatistics {
+ public:
+  HeapStatistics();
+  size_t total_heap_size() { return total_heap_size_; }
+  size_t total_heap_size_executable() { return total_heap_size_executable_; }
+  size_t total_physical_size() { return total_physical_size_; }
+  size_t total_available_size() { return total_available_size_; }
+  size_t used_heap_size() { return used_heap_size_; }
+  size_t heap_size_limit() { return heap_size_limit_; }
+
+ private:
+  size_t total_heap_size_;
+  size_t total_heap_size_executable_;
+  size_t total_physical_size_;
+  size_t total_available_size_;
+  size_t used_heap_size_;
+  size_t heap_size_limit_;
+
+  friend class V8;
+  friend class Isolate;
+};
+
+
+class V8_EXPORT HeapSpaceStatistics {
+ public:
+  HeapSpaceStatistics();
+  const char* space_name() { return space_name_; }
+  size_t space_size() { return space_size_; }
+  size_t space_used_size() { return space_used_size_; }
+  size_t space_available_size() { return space_available_size_; }
+  size_t physical_space_size() { return physical_space_size_; }
+
+ private:
+  const char* space_name_;
+  size_t space_size_;
+  size_t space_used_size_;
+  size_t space_available_size_;
+  size_t physical_space_size_;
+
+  friend class Isolate;
+};
+
+
+class V8_EXPORT HeapObjectStatistics {
+ public:
+  HeapObjectStatistics();
+  const char* object_type() { return object_type_; }
+  const char* object_sub_type() { return object_sub_type_; }
+  size_t object_count() { return object_count_; }
+  size_t object_size() { return object_size_; }
+
+ private:
+  const char* object_type_;
+  const char* object_sub_type_;
+  size_t object_count_;
+  size_t object_size_;
+
+  friend class Isolate;
+};
+
+
+class RetainedObjectInfo;
+
+
+/**
+ * FunctionEntryHook is the type of the profile entry hook called at entry to
+ * any generated function when function-level profiling is enabled.
+ *
+ * \param function the address of the function that's being entered.
+ * \param return_addr_location points to a location on stack where the machine
+ *    return address resides. This can be used to identify the caller of
+ *    \p function, and/or modified to divert execution when \p function exits.
+ *
+ * \note the entry hook must not cause garbage collection.
+ */
+typedef void (*FunctionEntryHook)(uintptr_t function,
+                                  uintptr_t return_addr_location);
+
+/**
+ * A JIT code event is issued each time code is added, moved or removed.
+ *
+ * \note removal events are not currently issued.
+ */
+struct JitCodeEvent {
+  enum EventType {
+    CODE_ADDED,
+    CODE_MOVED,
+    CODE_REMOVED,
+    CODE_ADD_LINE_POS_INFO,
+    CODE_START_LINE_INFO_RECORDING,
+    CODE_END_LINE_INFO_RECORDING
+  };
+  // Definition of the code position type. The "POSITION" type means the place
+  // in the source code which are of interest when making stack traces to
+  // pin-point the source location of a stack frame as close as possible.
+  // The "STATEMENT_POSITION" means the place at the beginning of each
+  // statement, and is used to indicate possible break locations.
+  enum PositionType { POSITION, STATEMENT_POSITION };
+
+  // Type of event.
+  EventType type;
+  // Start of the instructions.
+  void* code_start;
+  // Size of the instructions.
+  size_t code_len;
+  // Script info for CODE_ADDED event.
+  Local<UnboundScript> script;
+  // User-defined data for *_LINE_INFO_* event. It's used to hold the source
+  // code line information which is returned from the
+  // CODE_START_LINE_INFO_RECORDING event. And it's passed to subsequent
+  // CODE_ADD_LINE_POS_INFO and CODE_END_LINE_INFO_RECORDING events.
+  void* user_data;
+
+  struct name_t {
+    // Name of the object associated with the code, note that the string is not
+    // zero-terminated.
+    const char* str;
+    // Number of chars in str.
+    size_t len;
+  };
+
+  struct line_info_t {
+    // PC offset
+    size_t offset;
+    // Code postion
+    size_t pos;
+    // The position type.
+    PositionType position_type;
+  };
+
+  union {
+    // Only valid for CODE_ADDED.
+    struct name_t name;
+
+    // Only valid for CODE_ADD_LINE_POS_INFO
+    struct line_info_t line_info;
+
+    // New location of instructions. Only valid for CODE_MOVED.
+    void* new_code_start;
+  };
+};
+
+/**
+ * Option flags passed to the SetJitCodeEventHandler function.
+ */
+enum JitCodeEventOptions {
+  kJitCodeEventDefault = 0,
+  // Generate callbacks for already existent code.
+  kJitCodeEventEnumExisting = 1
+};
+
+
+/**
+ * Callback function passed to SetJitCodeEventHandler.
+ *
+ * \param event code add, move or removal event.
+ */
+typedef void (*JitCodeEventHandler)(const JitCodeEvent* event);
+
+
+/**
+ * Interface for iterating through all external resources in the heap.
+ */
+class V8_EXPORT ExternalResourceVisitor {  // NOLINT
+ public:
+  virtual ~ExternalResourceVisitor() {}
+  virtual void VisitExternalString(Local<String> string) {}
+};
+
+
+/**
+ * Interface for iterating through all the persistent handles in the heap.
+ */
+class V8_EXPORT PersistentHandleVisitor {  // NOLINT
+ public:
+  virtual ~PersistentHandleVisitor() {}
+  virtual void VisitPersistentHandle(Persistent<Value>* value,
+                                     uint16_t class_id) {}
+};
+
+
+/**
+ * Isolate represents an isolated instance of the V8 engine.  V8 isolates have
+ * completely separate states.  Objects from one isolate must not be used in
+ * other isolates.  The embedder can create multiple isolates and use them in
+ * parallel in multiple threads.  An isolate can be entered by at most one
+ * thread at any given time.  The Locker/Unlocker API must be used to
+ * synchronize.
+ */
+class V8_EXPORT Isolate {
+ public:
+  /**
+   * Initial configuration parameters for a new Isolate.
+   */
+  struct CreateParams {
+    CreateParams()
+        : entry_hook(NULL),
+          code_event_handler(NULL),
+          snapshot_blob(NULL),
+          counter_lookup_callback(NULL),
+          create_histogram_callback(NULL),
+          add_histogram_sample_callback(NULL),
+          array_buffer_allocator(NULL) {}
+
+    /**
+     * The optional entry_hook allows the host application to provide the
+     * address of a function that's invoked on entry to every V8-generated
+     * function.  Note that entry_hook is invoked at the very start of each
+     * generated function. Furthermore, if an  entry_hook is given, V8 will
+     * always run without a context snapshot.
+     */
+    FunctionEntryHook entry_hook;
+
+    /**
+     * Allows the host application to provide the address of a function that is
+     * notified each time code is added, moved or removed.
+     */
+    JitCodeEventHandler code_event_handler;
+
+    /**
+     * ResourceConstraints to use for the new Isolate.
+     */
+    ResourceConstraints constraints;
+
+    /**
+     * Explicitly specify a startup snapshot blob. The embedder owns the blob.
+     */
+    StartupData* snapshot_blob;
+
+
+    /**
+     * Enables the host application to provide a mechanism for recording
+     * statistics counters.
+     */
+    CounterLookupCallback counter_lookup_callback;
+
+    /**
+     * Enables the host application to provide a mechanism for recording
+     * histograms. The CreateHistogram function returns a
+     * histogram which will later be passed to the AddHistogramSample
+     * function.
+     */
+    CreateHistogramCallback create_histogram_callback;
+    AddHistogramSampleCallback add_histogram_sample_callback;
+
+    /**
+     * The ArrayBuffer::Allocator to use for allocating and freeing the backing
+     * store of ArrayBuffers.
+     */
+    ArrayBuffer::Allocator* array_buffer_allocator;
+  };
+
+
+  /**
+   * Stack-allocated class which sets the isolate for all operations
+   * executed within a local scope.
+   */
+  class V8_EXPORT Scope {
+   public:
+    explicit Scope(Isolate* isolate) : isolate_(isolate) {
+      isolate->Enter();
+    }
+
+    ~Scope() { isolate_->Exit(); }
+
+   private:
+    Isolate* const isolate_;
+
+    // Prevent copying of Scope objects.
+    Scope(const Scope&);
+    Scope& operator=(const Scope&);
+  };
+
+
+  /**
+   * Assert that no Javascript code is invoked.
+   */
+  class V8_EXPORT DisallowJavascriptExecutionScope {
+   public:
+    enum OnFailure { CRASH_ON_FAILURE, THROW_ON_FAILURE };
+
+    DisallowJavascriptExecutionScope(Isolate* isolate, OnFailure on_failure);
+    ~DisallowJavascriptExecutionScope();
+
+   private:
+    bool on_failure_;
+    void* internal_;
+
+    // Prevent copying of Scope objects.
+    DisallowJavascriptExecutionScope(const DisallowJavascriptExecutionScope&);
+    DisallowJavascriptExecutionScope& operator=(
+        const DisallowJavascriptExecutionScope&);
+  };
+
+
+  /**
+   * Introduce exception to DisallowJavascriptExecutionScope.
+   */
+  class V8_EXPORT AllowJavascriptExecutionScope {
+   public:
+    explicit AllowJavascriptExecutionScope(Isolate* isolate);
+    ~AllowJavascriptExecutionScope();
+
+   private:
+    void* internal_throws_;
+    void* internal_assert_;
+
+    // Prevent copying of Scope objects.
+    AllowJavascriptExecutionScope(const AllowJavascriptExecutionScope&);
+    AllowJavascriptExecutionScope& operator=(
+        const AllowJavascriptExecutionScope&);
+  };
+
+  /**
+   * Do not run microtasks while this scope is active, even if microtasks are
+   * automatically executed otherwise.
+   */
+  class V8_EXPORT SuppressMicrotaskExecutionScope {
+   public:
+    explicit SuppressMicrotaskExecutionScope(Isolate* isolate);
+    ~SuppressMicrotaskExecutionScope();
+
+   private:
+    internal::Isolate* isolate_;
+
+    // Prevent copying of Scope objects.
+    SuppressMicrotaskExecutionScope(const SuppressMicrotaskExecutionScope&);
+    SuppressMicrotaskExecutionScope& operator=(
+        const SuppressMicrotaskExecutionScope&);
+  };
+
+  /**
+   * Types of garbage collections that can be requested via
+   * RequestGarbageCollectionForTesting.
+   */
+  enum GarbageCollectionType {
+    kFullGarbageCollection,
+    kMinorGarbageCollection
+  };
+
+  /**
+   * Features reported via the SetUseCounterCallback callback. Do not change
+   * assigned numbers of existing items; add new features to the end of this
+   * list.
+   */
+  enum UseCounterFeature {
+    kUseAsm = 0,
+    kBreakIterator = 1,
+    kLegacyConst = 2,
+    kMarkDequeOverflow = 3,
+    kStoreBufferOverflow = 4,
+    kSlotsBufferOverflow = 5,
+    kObjectObserve = 6,
+    kForcedGC = 7,
+    kUseCounterFeatureCount  // This enum value must be last.
+  };
+
+  typedef void (*UseCounterCallback)(Isolate* isolate,
+                                     UseCounterFeature feature);
+
+
+  /**
+   * Creates a new isolate.  Does not change the currently entered
+   * isolate.
+   *
+   * When an isolate is no longer used its resources should be freed
+   * by calling Dispose().  Using the delete operator is not allowed.
+   *
+   * V8::Initialize() must have run prior to this.
+   */
+  static Isolate* New(const CreateParams& params);
+
+  /**
+   * Returns the entered isolate for the current thread or NULL in
+   * case there is no current isolate.
+   *
+   * This method must not be invoked before V8::Initialize() was invoked.
+   */
+  static Isolate* GetCurrent();
+
+  /**
+   * Custom callback used by embedders to help V8 determine if it should abort
+   * when it throws and no internal handler is predicted to catch the
+   * exception. If --abort-on-uncaught-exception is used on the command line,
+   * then V8 will abort if either:
+   * - no custom callback is set.
+   * - the custom callback set returns true.
+   * Otherwise, the custom callback will not be called and V8 will not abort.
+   */
+  typedef bool (*AbortOnUncaughtExceptionCallback)(Isolate*);
+  void SetAbortOnUncaughtExceptionCallback(
+      AbortOnUncaughtExceptionCallback callback);
+
+  /**
+   * Methods below this point require holding a lock (using Locker) in
+   * a multi-threaded environment.
+   */
+
+  /**
+   * Sets this isolate as the entered one for the current thread.
+   * Saves the previously entered one (if any), so that it can be
+   * restored when exiting.  Re-entering an isolate is allowed.
+   */
+  void Enter();
+
+  /**
+   * Exits this isolate by restoring the previously entered one in the
+   * current thread.  The isolate may still stay the same, if it was
+   * entered more than once.
+   *
+   * Requires: this == Isolate::GetCurrent().
+   */
+  void Exit();
+
+  /**
+   * Disposes the isolate.  The isolate must not be entered by any
+   * thread to be disposable.
+   */
+  void Dispose();
+
+  /**
+   * Associate embedder-specific data with the isolate. |slot| has to be
+   * between 0 and GetNumberOfDataSlots() - 1.
+   */
+  V8_INLINE void SetData(uint32_t slot, void* data);
+
+  /**
+   * Retrieve embedder-specific data from the isolate.
+   * Returns NULL if SetData has never been called for the given |slot|.
+   */
+  V8_INLINE void* GetData(uint32_t slot);
+
+  /**
+   * Returns the maximum number of available embedder data slots. Valid slots
+   * are in the range of 0 - GetNumberOfDataSlots() - 1.
+   */
+  V8_INLINE static uint32_t GetNumberOfDataSlots();
+
+  /**
+   * Get statistics about the heap memory usage.
+   */
+  void GetHeapStatistics(HeapStatistics* heap_statistics);
+
+  /**
+   * Returns the number of spaces in the heap.
+   */
+  size_t NumberOfHeapSpaces();
+
+  /**
+   * Get the memory usage of a space in the heap.
+   *
+   * \param space_statistics The HeapSpaceStatistics object to fill in
+   *   statistics.
+   * \param index The index of the space to get statistics from, which ranges
+   *   from 0 to NumberOfHeapSpaces() - 1.
+   * \returns true on success.
+   */
+  bool GetHeapSpaceStatistics(HeapSpaceStatistics* space_statistics,
+                              size_t index);
+
+  /**
+   * Returns the number of types of objects tracked in the heap at GC.
+   */
+  size_t NumberOfTrackedHeapObjectTypes();
+
+  /**
+   * Get statistics about objects in the heap.
+   *
+   * \param object_statistics The HeapObjectStatistics object to fill in
+   *   statistics of objects of given type, which were live in the previous GC.
+   * \param type_index The index of the type of object to fill details about,
+   *   which ranges from 0 to NumberOfTrackedHeapObjectTypes() - 1.
+   * \returns true on success.
+   */
+  bool GetHeapObjectStatisticsAtLastGC(HeapObjectStatistics* object_statistics,
+                                       size_t type_index);
+
+  /**
+   * Get a call stack sample from the isolate.
+   * \param state Execution state.
+   * \param frames Caller allocated buffer to store stack frames.
+   * \param frames_limit Maximum number of frames to capture. The buffer must
+   *                     be large enough to hold the number of frames.
+   * \param sample_info The sample info is filled up by the function
+   *                    provides number of actual captured stack frames and
+   *                    the current VM state.
+   * \note GetStackSample should only be called when the JS thread is paused or
+   *       interrupted. Otherwise the behavior is undefined.
+   */
+  void GetStackSample(const RegisterState& state, void** frames,
+                      size_t frames_limit, SampleInfo* sample_info);
+
+  /**
+   * Adjusts the amount of registered external memory. Used to give V8 an
+   * indication of the amount of externally allocated memory that is kept alive
+   * by JavaScript objects. V8 uses this to decide when to perform global
+   * garbage collections. Registering externally allocated memory will trigger
+   * global garbage collections more often than it would otherwise in an attempt
+   * to garbage collect the JavaScript objects that keep the externally
+   * allocated memory alive.
+   *
+   * \param change_in_bytes the change in externally allocated memory that is
+   *   kept alive by JavaScript objects.
+   * \returns the adjusted value.
+   */
+  V8_INLINE int64_t
+      AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes);
+
+  /**
+   * Returns heap profiler for this isolate. Will return NULL until the isolate
+   * is initialized.
+   */
+  HeapProfiler* GetHeapProfiler();
+
+  /**
+   * Returns CPU profiler for this isolate. Will return NULL unless the isolate
+   * is initialized. It is the embedder's responsibility to stop all CPU
+   * profiling activities if it has started any.
+   */
+  CpuProfiler* GetCpuProfiler();
+
+  /** Returns true if this isolate has a current context. */
+  bool InContext();
+
+  /** Returns the context that is on the top of the stack. */
+  Local<Context> GetCurrentContext();
+
+  /**
+   * Returns the context of the calling JavaScript code.  That is the
+   * context of the top-most JavaScript frame.  If there are no
+   * JavaScript frames an empty handle is returned.
+   */
+  Local<Context> GetCallingContext();
+
+  /** Returns the last entered context. */
+  Local<Context> GetEnteredContext();
+
+  /**
+   * Schedules an exception to be thrown when returning to JavaScript.  When an
+   * exception has been scheduled it is illegal to invoke any JavaScript
+   * operation; the caller must return immediately and only after the exception
+   * has been handled does it become legal to invoke JavaScript operations.
+   */
+  Local<Value> ThrowException(Local<Value> exception);
+
+  /**
+   * Allows the host application to group objects together. If one
+   * object in the group is alive, all objects in the group are alive.
+   * After each garbage collection, object groups are removed. It is
+   * intended to be used in the before-garbage-collection callback
+   * function, for instance to simulate DOM tree connections among JS
+   * wrapper objects. Object groups for all dependent handles need to
+   * be provided for kGCTypeMarkSweepCompact collections, for all other
+   * garbage collection types it is sufficient to provide object groups
+   * for partially dependent handles only.
+   */
+  template<typename T> void SetObjectGroupId(const Persistent<T>& object,
+                                             UniqueId id);
+
+  /**
+   * Allows the host application to declare implicit references from an object
+   * group to an object. If the objects of the object group are alive, the child
+   * object is alive too. After each garbage collection, all implicit references
+   * are removed. It is intended to be used in the before-garbage-collection
+   * callback function.
+   */
+  template<typename T> void SetReferenceFromGroup(UniqueId id,
+                                                  const Persistent<T>& child);
+
+  /**
+   * Allows the host application to declare implicit references from an object
+   * to another object. If the parent object is alive, the child object is alive
+   * too. After each garbage collection, all implicit references are removed. It
+   * is intended to be used in the before-garbage-collection callback function.
+   */
+  template<typename T, typename S>
+  void SetReference(const Persistent<T>& parent, const Persistent<S>& child);
+
+  V8_DEPRECATE_SOON("Use GCCallBack instead",
+                    typedef void (*GCPrologueCallback)(Isolate* isolate,
+                                                       GCType type,
+                                                       GCCallbackFlags flags));
+  V8_DEPRECATE_SOON("Use GCCallBack instead",
+                    typedef void (*GCEpilogueCallback)(Isolate* isolate,
+                                                       GCType type,
+                                                       GCCallbackFlags flags));
+  typedef void (*GCCallback)(Isolate* isolate, GCType type,
+                             GCCallbackFlags flags);
+
+  /**
+   * Enables the host application to receive a notification before a
+   * garbage collection. Allocations are allowed in the callback function,
+   * but the callback is not re-entrant: if the allocation inside it will
+   * trigger the garbage collection, the callback won't be called again.
+   * It is possible to specify the GCType filter for your callback. But it is
+   * not possible to register the same callback function two times with
+   * different GCType filters.
+   */
+  void AddGCPrologueCallback(GCCallback callback,
+                             GCType gc_type_filter = kGCTypeAll);
+
+  /**
+   * This function removes callback which was installed by
+   * AddGCPrologueCallback function.
+   */
+  void RemoveGCPrologueCallback(GCCallback callback);
+
+  /**
+   * Enables the host application to receive a notification after a
+   * garbage collection. Allocations are allowed in the callback function,
+   * but the callback is not re-entrant: if the allocation inside it will
+   * trigger the garbage collection, the callback won't be called again.
+   * It is possible to specify the GCType filter for your callback. But it is
+   * not possible to register the same callback function two times with
+   * different GCType filters.
+   */
+  void AddGCEpilogueCallback(GCCallback callback,
+                             GCType gc_type_filter = kGCTypeAll);
+
+  /**
+   * This function removes callback which was installed by
+   * AddGCEpilogueCallback function.
+   */
+  void RemoveGCEpilogueCallback(GCCallback callback);
+
+  /**
+   * Forcefully terminate the current thread of JavaScript execution
+   * in the given isolate.
+   *
+   * This method can be used by any thread even if that thread has not
+   * acquired the V8 lock with a Locker object.
+   */
+  void TerminateExecution();
+
+  /**
+   * Is V8 terminating JavaScript execution.
+   *
+   * Returns true if JavaScript execution is currently terminating
+   * because of a call to TerminateExecution.  In that case there are
+   * still JavaScript frames on the stack and the termination
+   * exception is still active.
+   */
+  bool IsExecutionTerminating();
+
+  /**
+   * Resume execution capability in the given isolate, whose execution
+   * was previously forcefully terminated using TerminateExecution().
+   *
+   * When execution is forcefully terminated using TerminateExecution(),
+   * the isolate can not resume execution until all JavaScript frames
+   * have propagated the uncatchable exception which is generated.  This
+   * method allows the program embedding the engine to handle the
+   * termination event and resume execution capability, even if
+   * JavaScript frames remain on the stack.
+   *
+   * This method can be used by any thread even if that thread has not
+   * acquired the V8 lock with a Locker object.
+   */
+  void CancelTerminateExecution();
+
+  /**
+   * Request V8 to interrupt long running JavaScript code and invoke
+   * the given |callback| passing the given |data| to it. After |callback|
+   * returns control will be returned to the JavaScript code.
+   * There may be a number of interrupt requests in flight.
+   * Can be called from another thread without acquiring a |Locker|.
+   * Registered |callback| must not reenter interrupted Isolate.
+   */
+  void RequestInterrupt(InterruptCallback callback, void* data);
+
+  /**
+   * Request garbage collection in this Isolate. It is only valid to call this
+   * function if --expose_gc was specified.
+   *
+   * This should only be used for testing purposes and not to enforce a garbage
+   * collection schedule. It has strong negative impact on the garbage
+   * collection performance. Use IdleNotificationDeadline() or
+   * LowMemoryNotification() instead to influence the garbage collection
+   * schedule.
+   */
+  void RequestGarbageCollectionForTesting(GarbageCollectionType type);
+
+  /**
+   * Set the callback to invoke for logging event.
+   */
+  void SetEventLogger(LogEventCallback that);
+
+  /**
+   * Adds a callback to notify the host application when a script finished
+   * running.  If a script re-enters the runtime during executing, the
+   * CallCompletedCallback is only invoked when the outer-most script
+   * execution ends.  Executing scripts inside the callback do not trigger
+   * further callbacks.
+   */
+  void AddCallCompletedCallback(CallCompletedCallback callback);
+
+  /**
+   * Removes callback that was installed by AddCallCompletedCallback.
+   */
+  void RemoveCallCompletedCallback(CallCompletedCallback callback);
+
+
+  /**
+   * Set callback to notify about promise reject with no handler, or
+   * revocation of such a previous notification once the handler is added.
+   */
+  void SetPromiseRejectCallback(PromiseRejectCallback callback);
+
+  /**
+   * Experimental: Runs the Microtask Work Queue until empty
+   * Any exceptions thrown by microtask callbacks are swallowed.
+   */
+  void RunMicrotasks();
+
+  /**
+   * Experimental: Enqueues the callback to the Microtask Work Queue
+   */
+  void EnqueueMicrotask(Local<Function> microtask);
+
+  /**
+   * Experimental: Enqueues the callback to the Microtask Work Queue
+   */
+  void EnqueueMicrotask(MicrotaskCallback microtask, void* data = NULL);
+
+   /**
+   * Experimental: Controls whether the Microtask Work Queue is automatically
+   * run when the script call depth decrements to zero.
+   */
+  void SetAutorunMicrotasks(bool autorun);
+
+  /**
+   * Experimental: Returns whether the Microtask Work Queue is automatically
+   * run when the script call depth decrements to zero.
+   */
+  bool WillAutorunMicrotasks() const;
+
+  /**
+   * Sets a callback for counting the number of times a feature of V8 is used.
+   */
+  void SetUseCounterCallback(UseCounterCallback callback);
+
+  /**
+   * Enables the host application to provide a mechanism for recording
+   * statistics counters.
+   */
+  void SetCounterFunction(CounterLookupCallback);
+
+  /**
+   * Enables the host application to provide a mechanism for recording
+   * histograms. The CreateHistogram function returns a
+   * histogram which will later be passed to the AddHistogramSample
+   * function.
+   */
+  void SetCreateHistogramFunction(CreateHistogramCallback);
+  void SetAddHistogramSampleFunction(AddHistogramSampleCallback);
+
+  /**
+   * Optional notification that the embedder is idle.
+   * V8 uses the notification to perform garbage collection.
+   * This call can be used repeatedly if the embedder remains idle.
+   * Returns true if the embedder should stop calling IdleNotificationDeadline
+   * until real work has been done.  This indicates that V8 has done
+   * as much cleanup as it will be able to do.
+   *
+   * The deadline_in_seconds argument specifies the deadline V8 has to finish
+   * garbage collection work. deadline_in_seconds is compared with
+   * MonotonicallyIncreasingTime() and should be based on the same timebase as
+   * that function. There is no guarantee that the actual work will be done
+   * within the time limit.
+   */
+  bool IdleNotificationDeadline(double deadline_in_seconds);
+
+  V8_DEPRECATE_SOON("use IdleNotificationDeadline()",
+                    bool IdleNotification(int idle_time_in_ms));
+
+  /**
+   * Optional notification that the system is running low on memory.
+   * V8 uses these notifications to attempt to free memory.
+   */
+  void LowMemoryNotification();
+
+  /**
+   * Optional notification that a context has been disposed. V8 uses
+   * these notifications to guide the GC heuristic. Returns the number
+   * of context disposals - including this one - since the last time
+   * V8 had a chance to clean up.
+   *
+   * The optional parameter |dependant_context| specifies whether the disposed
+   * context was depending on state from other contexts or not.
+   */
+  int ContextDisposedNotification(bool dependant_context = true);
+
+  /**
+   * Optional notification that the isolate switched to the foreground.
+   * V8 uses these notifications to guide heuristics.
+   */
+  void IsolateInForegroundNotification();
+
+  /**
+   * Optional notification that the isolate switched to the background.
+   * V8 uses these notifications to guide heuristics.
+   */
+  void IsolateInBackgroundNotification();
+
+  /**
+   * Allows the host application to provide the address of a function that is
+   * notified each time code is added, moved or removed.
+   *
+   * \param options options for the JIT code event handler.
+   * \param event_handler the JIT code event handler, which will be invoked
+   *     each time code is added, moved or removed.
+   * \note \p event_handler won't get notified of existent code.
+   * \note since code removal notifications are not currently issued, the
+   *     \p event_handler may get notifications of code that overlaps earlier
+   *     code notifications. This happens when code areas are reused, and the
+   *     earlier overlapping code areas should therefore be discarded.
+   * \note the events passed to \p event_handler and the strings they point to
+   *     are not guaranteed to live past each call. The \p event_handler must
+   *     copy strings and other parameters it needs to keep around.
+   * \note the set of events declared in JitCodeEvent::EventType is expected to
+   *     grow over time, and the JitCodeEvent structure is expected to accrue
+   *     new members. The \p event_handler function must ignore event codes
+   *     it does not recognize to maintain future compatibility.
+   * \note Use Isolate::CreateParams to get events for code executed during
+   *     Isolate setup.
+   */
+  void SetJitCodeEventHandler(JitCodeEventOptions options,
+                              JitCodeEventHandler event_handler);
+
+  /**
+   * Modifies the stack limit for this Isolate.
+   *
+   * \param stack_limit An address beyond which the Vm's stack may not grow.
+   *
+   * \note  If you are using threads then you should hold the V8::Locker lock
+   *     while setting the stack limit and you must set a non-default stack
+   *     limit separately for each thread.
+   */
+  void SetStackLimit(uintptr_t stack_limit);
+
+  /**
+   * Returns a memory range that can potentially contain jitted code.
+   *
+   * On Win64, embedders are advised to install function table callbacks for
+   * these ranges, as default SEH won't be able to unwind through jitted code.
+   *
+   * The first page of the code range is reserved for the embedder and is
+   * committed, writable, and executable.
+   *
+   * Might be empty on other platforms.
+   *
+   * https://code.google.com/p/v8/issues/detail?id=3598
+   */
+  void GetCodeRange(void** start, size_t* length_in_bytes);
+
+  /** Set the callback to invoke in case of fatal errors. */
+  void SetFatalErrorHandler(FatalErrorCallback that);
+
+  /**
+   * Set the callback to invoke to check if code generation from
+   * strings should be allowed.
+   */
+  void SetAllowCodeGenerationFromStringsCallback(
+      AllowCodeGenerationFromStringsCallback callback);
+
+  /**
+  * Check if V8 is dead and therefore unusable.  This is the case after
+  * fatal errors such as out-of-memory situations.
+  */
+  bool IsDead();
+
+  /**
+   * Adds a message listener.
+   *
+   * The same message listener can be added more than once and in that
+   * case it will be called more than once for each message.
+   *
+   * If data is specified, it will be passed to the callback when it is called.
+   * Otherwise, the exception object will be passed to the callback instead.
+   */
+  bool AddMessageListener(MessageCallback that,
+                          Local<Value> data = Local<Value>());
+
+  /**
+   * Remove all message listeners from the specified callback function.
+   */
+  void RemoveMessageListeners(MessageCallback that);
+
+  /** Callback function for reporting failed access checks.*/
+  void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback);
+
+  /**
+   * Tells V8 to capture current stack trace when uncaught exception occurs
+   * and report it to the message listeners. The option is off by default.
+   */
+  void SetCaptureStackTraceForUncaughtExceptions(
+      bool capture, int frame_limit = 10,
+      StackTrace::StackTraceOptions options = StackTrace::kOverview);
+
+  /**
+   * Enables the host application to provide a mechanism to be notified
+   * and perform custom logging when V8 Allocates Executable Memory.
+   */
+  void AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+                                   ObjectSpace space, AllocationAction action);
+
+  /**
+   * Removes callback that was installed by AddMemoryAllocationCallback.
+   */
+  void RemoveMemoryAllocationCallback(MemoryAllocationCallback callback);
+
+  /**
+   * Iterates through all external resources referenced from current isolate
+   * heap.  GC is not invoked prior to iterating, therefore there is no
+   * guarantee that visited objects are still alive.
+   */
+  void VisitExternalResources(ExternalResourceVisitor* visitor);
+
+  /**
+   * Iterates through all the persistent handles in the current isolate's heap
+   * that have class_ids.
+   */
+  void VisitHandlesWithClassIds(PersistentHandleVisitor* visitor);
+
+  /**
+   * Iterates through all the persistent handles in the current isolate's heap
+   * that have class_ids and are candidates to be marked as partially dependent
+   * handles. This will visit handles to young objects created since the last
+   * garbage collection but is free to visit an arbitrary superset of these
+   * objects.
+   */
+  void VisitHandlesForPartialDependence(PersistentHandleVisitor* visitor);
+
+  /**
+   * Iterates through all the persistent handles in the current isolate's heap
+   * that have class_ids and are weak to be marked as inactive if there is no
+   * pending activity for the handle.
+   */
+  void VisitWeakHandles(PersistentHandleVisitor* visitor);
+
+ private:
+  template <class K, class V, class Traits>
+  friend class PersistentValueMapBase;
+
+  Isolate();
+  Isolate(const Isolate&);
+  ~Isolate();
+  Isolate& operator=(const Isolate&);
+  void* operator new(size_t size);
+  void operator delete(void*, size_t);
+
+  void SetObjectGroupId(internal::Object** object, UniqueId id);
+  void SetReferenceFromGroup(UniqueId id, internal::Object** object);
+  void SetReference(internal::Object** parent, internal::Object** child);
+  void ReportExternalAllocationLimitReached();
+};
+
+class V8_EXPORT StartupData {
+ public:
+  const char* data;
+  int raw_size;
+};
+
+
+/**
+ * EntropySource is used as a callback function when v8 needs a source
+ * of entropy.
+ */
+typedef bool (*EntropySource)(unsigned char* buffer, size_t length);
+
+
+/**
+ * ReturnAddressLocationResolver is used as a callback function when v8 is
+ * resolving the location of a return address on the stack. Profilers that
+ * change the return address on the stack can use this to resolve the stack
+ * location to whereever the profiler stashed the original return address.
+ *
+ * \param return_addr_location points to a location on stack where a machine
+ *    return address resides.
+ * \returns either return_addr_location, or else a pointer to the profiler's
+ *    copy of the original return address.
+ *
+ * \note the resolver function must not cause garbage collection.
+ */
+typedef uintptr_t (*ReturnAddressLocationResolver)(
+    uintptr_t return_addr_location);
+
+
+/**
+ * Container class for static utility functions.
+ */
+class V8_EXPORT V8 {
+ public:
+  /** Set the callback to invoke in case of fatal errors. */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void SetFatalErrorHandler(FatalErrorCallback that));
+
+  /**
+   * Set the callback to invoke to check if code generation from
+   * strings should be allowed.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version", void SetAllowCodeGenerationFromStringsCallback(
+                                 AllowCodeGenerationFromStringsCallback that));
+
+  /**
+  * Check if V8 is dead and therefore unusable.  This is the case after
+  * fatal errors such as out-of-memory situations.
+  */
+  V8_INLINE static V8_DEPRECATE_SOON("no alternative", bool IsDead());
+
+  /**
+   * Hand startup data to V8, in case the embedder has chosen to build
+   * V8 with external startup data.
+   *
+   * Note:
+   * - By default the startup data is linked into the V8 library, in which
+   *   case this function is not meaningful.
+   * - If this needs to be called, it needs to be called before V8
+   *   tries to make use of its built-ins.
+   * - To avoid unnecessary copies of data, V8 will point directly into the
+   *   given data blob, so pretty please keep it around until V8 exit.
+   * - Compression of the startup blob might be useful, but needs to
+   *   handled entirely on the embedders' side.
+   * - The call will abort if the data is invalid.
+   */
+  static void SetNativesDataBlob(StartupData* startup_blob);
+  static void SetSnapshotDataBlob(StartupData* startup_blob);
+
+  /**
+   * Create a new isolate and context for the purpose of capturing a snapshot
+   * Returns { NULL, 0 } on failure.
+   * The caller owns the data array in the return value.
+   */
+  static StartupData CreateSnapshotDataBlob(const char* custom_source = NULL);
+
+  /**
+   * Adds a message listener.
+   *
+   * The same message listener can be added more than once and in that
+   * case it will be called more than once for each message.
+   *
+   * If data is specified, it will be passed to the callback when it is called.
+   * Otherwise, the exception object will be passed to the callback instead.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      bool AddMessageListener(MessageCallback that,
+                              Local<Value> data = Local<Value>()));
+
+  /**
+   * Remove all message listeners from the specified callback function.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version", void RemoveMessageListeners(MessageCallback that));
+
+  /**
+   * Tells V8 to capture current stack trace when uncaught exception occurs
+   * and report it to the message listeners. The option is off by default.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void SetCaptureStackTraceForUncaughtExceptions(
+          bool capture, int frame_limit = 10,
+          StackTrace::StackTraceOptions options = StackTrace::kOverview));
+
+  /**
+   * Sets V8 flags from a string.
+   */
+  static void SetFlagsFromString(const char* str, int length);
+
+  /**
+   * Sets V8 flags from the command line.
+   */
+  static void SetFlagsFromCommandLine(int* argc,
+                                      char** argv,
+                                      bool remove_flags);
+
+  /** Get the version string. */
+  static const char* GetVersion();
+
+  /** Callback function for reporting failed access checks.*/
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback));
+
+  /**
+   * Enables the host application to receive a notification before a
+   * garbage collection.  Allocations are not allowed in the
+   * callback function, you therefore cannot manipulate objects (set
+   * or delete properties for example) since it is possible such
+   * operations will result in the allocation of objects. It is possible
+   * to specify the GCType filter for your callback. But it is not possible to
+   * register the same callback function two times with different
+   * GCType filters.
+   */
+  static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void AddGCPrologueCallback(GCCallback callback,
+                                 GCType gc_type_filter = kGCTypeAll));
+
+  /**
+   * This function removes callback which was installed by
+   * AddGCPrologueCallback function.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void RemoveGCPrologueCallback(GCCallback callback));
+
+  /**
+   * Enables the host application to receive a notification after a
+   * garbage collection.  Allocations are not allowed in the
+   * callback function, you therefore cannot manipulate objects (set
+   * or delete properties for example) since it is possible such
+   * operations will result in the allocation of objects. It is possible
+   * to specify the GCType filter for your callback. But it is not possible to
+   * register the same callback function two times with different
+   * GCType filters.
+   */
+  static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void AddGCEpilogueCallback(GCCallback callback,
+                                 GCType gc_type_filter = kGCTypeAll));
+
+  /**
+   * This function removes callback which was installed by
+   * AddGCEpilogueCallback function.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void RemoveGCEpilogueCallback(GCCallback callback));
+
+  /**
+   * Enables the host application to provide a mechanism to be notified
+   * and perform custom logging when V8 Allocates Executable Memory.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+                                       ObjectSpace space,
+                                       AllocationAction action));
+
+  /**
+   * Removes callback that was installed by AddMemoryAllocationCallback.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void RemoveMemoryAllocationCallback(MemoryAllocationCallback callback));
+
+  /**
+   * Initializes V8. This function needs to be called before the first Isolate
+   * is created. It always returns true.
+   */
+  static bool Initialize();
+
+  /**
+   * Allows the host application to provide a callback which can be used
+   * as a source of entropy for random number generators.
+   */
+  static void SetEntropySource(EntropySource source);
+
+  /**
+   * Allows the host application to provide a callback that allows v8 to
+   * cooperate with a profiler that rewrites return addresses on stack.
+   */
+  static void SetReturnAddressLocationResolver(
+      ReturnAddressLocationResolver return_address_resolver);
+
+  /**
+   * Forcefully terminate the current thread of JavaScript execution
+   * in the given isolate.
+   *
+   * This method can be used by any thread even if that thread has not
+   * acquired the V8 lock with a Locker object.
+   *
+   * \param isolate The isolate in which to terminate the current JS execution.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON("Use isolate version",
+                                     void TerminateExecution(Isolate* isolate));
+
+  /**
+   * Is V8 terminating JavaScript execution.
+   *
+   * Returns true if JavaScript execution is currently terminating
+   * because of a call to TerminateExecution.  In that case there are
+   * still JavaScript frames on the stack and the termination
+   * exception is still active.
+   *
+   * \param isolate The isolate in which to check.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      bool IsExecutionTerminating(Isolate* isolate = NULL));
+
+  /**
+   * Resume execution capability in the given isolate, whose execution
+   * was previously forcefully terminated using TerminateExecution().
+   *
+   * When execution is forcefully terminated using TerminateExecution(),
+   * the isolate can not resume execution until all JavaScript frames
+   * have propagated the uncatchable exception which is generated.  This
+   * method allows the program embedding the engine to handle the
+   * termination event and resume execution capability, even if
+   * JavaScript frames remain on the stack.
+   *
+   * This method can be used by any thread even if that thread has not
+   * acquired the V8 lock with a Locker object.
+   *
+   * \param isolate The isolate in which to resume execution capability.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version", void CancelTerminateExecution(Isolate* isolate));
+
+  /**
+   * Releases any resources used by v8 and stops any utility threads
+   * that may be running.  Note that disposing v8 is permanent, it
+   * cannot be reinitialized.
+   *
+   * It should generally not be necessary to dispose v8 before exiting
+   * a process, this should happen automatically.  It is only necessary
+   * to use if the process needs the resources taken up by v8.
+   */
+  static bool Dispose();
+
+  /**
+   * Iterates through all external resources referenced from current isolate
+   * heap.  GC is not invoked prior to iterating, therefore there is no
+   * guarantee that visited objects are still alive.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isoalte version",
+      void VisitExternalResources(ExternalResourceVisitor* visitor));
+
+  /**
+   * Iterates through all the persistent handles in the current isolate's heap
+   * that have class_ids.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void VisitHandlesWithClassIds(PersistentHandleVisitor* visitor));
+
+  /**
+   * Iterates through all the persistent handles in isolate's heap that have
+   * class_ids.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void VisitHandlesWithClassIds(Isolate* isolate,
+                                    PersistentHandleVisitor* visitor));
+
+  /**
+   * Iterates through all the persistent handles in the current isolate's heap
+   * that have class_ids and are candidates to be marked as partially dependent
+   * handles. This will visit handles to young objects created since the last
+   * garbage collection but is free to visit an arbitrary superset of these
+   * objects.
+   */
+  V8_INLINE static V8_DEPRECATE_SOON(
+      "Use isolate version",
+      void VisitHandlesForPartialDependence(Isolate* isolate,
+                                            PersistentHandleVisitor* visitor));
+
+  /**
+   * Initialize the ICU library bundled with V8. The embedder should only
+   * invoke this method when using the bundled ICU. Returns true on success.
+   *
+   * If V8 was compiled with the ICU data in an external file, the location
+   * of the data file has to be provided.
+   */
+  static bool InitializeICU(const char* icu_data_file = NULL);
+
+  /**
+   * Initialize the external startup data. The embedder only needs to
+   * invoke this method when external startup data was enabled in a build.
+   *
+   * If V8 was compiled with the startup data in an external file, then
+   * V8 needs to be given those external files during startup. There are
+   * three ways to do this:
+   * - InitializeExternalStartupData(const char*)
+   *   This will look in the given directory for files "natives_blob.bin"
+   *   and "snapshot_blob.bin" - which is what the default build calls them.
+   * - InitializeExternalStartupData(const char*, const char*)
+   *   As above, but will directly use the two given file names.
+   * - Call SetNativesDataBlob, SetNativesDataBlob.
+   *   This will read the blobs from the given data structures and will
+   *   not perform any file IO.
+   */
+  static void InitializeExternalStartupData(const char* directory_path);
+  static void InitializeExternalStartupData(const char* natives_blob,
+                                            const char* snapshot_blob);
+  /**
+   * Sets the v8::Platform to use. This should be invoked before V8 is
+   * initialized.
+   */
+  static void InitializePlatform(Platform* platform);
+
+  /**
+   * Clears all references to the v8::Platform. This should be invoked after
+   * V8 was disposed.
+   */
+  static void ShutdownPlatform();
+
+ private:
+  V8();
+
+  static internal::Object** GlobalizeReference(internal::Isolate* isolate,
+                                               internal::Object** handle);
+  static internal::Object** CopyPersistent(internal::Object** handle);
+  static void DisposeGlobal(internal::Object** global_handle);
+  typedef WeakCallbackData<Value, void>::Callback WeakCallback;
+  static void MakeWeak(internal::Object** global_handle, void* data,
+                       WeakCallback weak_callback);
+  static void MakeWeak(internal::Object** global_handle, void* data,
+                       WeakCallbackInfo<void>::Callback weak_callback,
+                       WeakCallbackType type);
+  static void MakeWeak(internal::Object** global_handle, void* data,
+                       // Must be 0 or -1.
+                       int internal_field_index1,
+                       // Must be 1 or -1.
+                       int internal_field_index2,
+                       WeakCallbackInfo<void>::Callback weak_callback);
+  static void* ClearWeak(internal::Object** global_handle);
+  static void Eternalize(Isolate* isolate,
+                         Value* handle,
+                         int* index);
+  static Local<Value> GetEternal(Isolate* isolate, int index);
+
+  static void FromJustIsNothing();
+  static void ToLocalEmpty();
+  static void InternalFieldOutOfBounds(int index);
+  template <class T> friend class Local;
+  template <class T>
+  friend class MaybeLocal;
+  template <class T>
+  friend class Maybe;
+  template <class T>
+  friend class WeakCallbackInfo;
+  template <class T> friend class Eternal;
+  template <class T> friend class PersistentBase;
+  template <class T, class M> friend class Persistent;
+  friend class Context;
+};
+
+
+/**
+ * A simple Maybe type, representing an object which may or may not have a
+ * value, see https://hackage.haskell.org/package/base/docs/Data-Maybe.html.
+ *
+ * If an API method returns a Maybe<>, the API method can potentially fail
+ * either because an exception is thrown, or because an exception is pending,
+ * e.g. because a previous API call threw an exception that hasn't been caught
+ * yet, or because a TerminateExecution exception was thrown. In that case, a
+ * "Nothing" value is returned.
+ */
+template <class T>
+class Maybe {
+ public:
+  V8_INLINE bool IsNothing() const { return !has_value; }
+  V8_INLINE bool IsJust() const { return has_value; }
+
+  // Will crash if the Maybe<> is nothing.
+  V8_INLINE T FromJust() const {
+    if (V8_UNLIKELY(!IsJust())) V8::FromJustIsNothing();
+    return value;
+  }
+
+  V8_INLINE T FromMaybe(const T& default_value) const {
+    return has_value ? value : default_value;
+  }
+
+  V8_INLINE bool operator==(const Maybe& other) const {
+    return (IsJust() == other.IsJust()) &&
+           (!IsJust() || FromJust() == other.FromJust());
+  }
+
+  V8_INLINE bool operator!=(const Maybe& other) const {
+    return !operator==(other);
+  }
+
+ private:
+  Maybe() : has_value(false) {}
+  explicit Maybe(const T& t) : has_value(true), value(t) {}
+
+  bool has_value;
+  T value;
+
+  template <class U>
+  friend Maybe<U> Nothing();
+  template <class U>
+  friend Maybe<U> Just(const U& u);
+};
+
+
+template <class T>
+inline Maybe<T> Nothing() {
+  return Maybe<T>();
+}
+
+
+template <class T>
+inline Maybe<T> Just(const T& t) {
+  return Maybe<T>(t);
+}
+
+
+/**
+ * An external exception handler.
+ */
+class V8_EXPORT TryCatch {
+ public:
+  /**
+   * Creates a new try/catch block and registers it with v8.  Note that
+   * all TryCatch blocks should be stack allocated because the memory
+   * location itself is compared against JavaScript try/catch blocks.
+   */
+  V8_DEPRECATE_SOON("Use isolate version", TryCatch());
+
+  /**
+   * Creates a new try/catch block and registers it with v8.  Note that
+   * all TryCatch blocks should be stack allocated because the memory
+   * location itself is compared against JavaScript try/catch blocks.
+   */
+  TryCatch(Isolate* isolate);
+
+  /**
+   * Unregisters and deletes this try/catch block.
+   */
+  ~TryCatch();
+
+  /**
+   * Returns true if an exception has been caught by this try/catch block.
+   */
+  bool HasCaught() const;
+
+  /**
+   * For certain types of exceptions, it makes no sense to continue execution.
+   *
+   * If CanContinue returns false, the correct action is to perform any C++
+   * cleanup needed and then return.  If CanContinue returns false and
+   * HasTerminated returns true, it is possible to call
+   * CancelTerminateExecution in order to continue calling into the engine.
+   */
+  bool CanContinue() const;
+
+  /**
+   * Returns true if an exception has been caught due to script execution
+   * being terminated.
+   *
+   * There is no JavaScript representation of an execution termination
+   * exception.  Such exceptions are thrown when the TerminateExecution
+   * methods are called to terminate a long-running script.
+   *
+   * If such an exception has been thrown, HasTerminated will return true,
+   * indicating that it is possible to call CancelTerminateExecution in order
+   * to continue calling into the engine.
+   */
+  bool HasTerminated() const;
+
+  /**
+   * Throws the exception caught by this TryCatch in a way that avoids
+   * it being caught again by this same TryCatch.  As with ThrowException
+   * it is illegal to execute any JavaScript operations after calling
+   * ReThrow; the caller must return immediately to where the exception
+   * is caught.
+   */
+  Local<Value> ReThrow();
+
+  /**
+   * Returns the exception caught by this try/catch block.  If no exception has
+   * been caught an empty handle is returned.
+   *
+   * The returned handle is valid until this TryCatch block has been destroyed.
+   */
+  Local<Value> Exception() const;
+
+  /**
+   * Returns the .stack property of the thrown object.  If no .stack
+   * property is present an empty handle is returned.
+   */
+  V8_DEPRECATE_SOON("Use maybe version.", Local<Value> StackTrace() const);
+  V8_WARN_UNUSED_RESULT MaybeLocal<Value> StackTrace(
+      Local<Context> context) const;
+
+  /**
+   * Returns the message associated with this exception.  If there is
+   * no message associated an empty handle is returned.
+   *
+   * The returned handle is valid until this TryCatch block has been
+   * destroyed.
+   */
+  Local<v8::Message> Message() const;
+
+  /**
+   * Clears any exceptions that may have been caught by this try/catch block.
+   * After this method has been called, HasCaught() will return false. Cancels
+   * the scheduled exception if it is caught and ReThrow() is not called before.
+   *
+   * It is not necessary to clear a try/catch block before using it again; if
+   * another exception is thrown the previously caught exception will just be
+   * overwritten.  However, it is often a good idea since it makes it easier
+   * to determine which operation threw a given exception.
+   */
+  void Reset();
+
+  /**
+   * Set verbosity of the external exception handler.
+   *
+   * By default, exceptions that are caught by an external exception
+   * handler are not reported.  Call SetVerbose with true on an
+   * external exception handler to have exceptions caught by the
+   * handler reported as if they were not caught.
+   */
+  void SetVerbose(bool value);
+
+  /**
+   * Set whether or not this TryCatch should capture a Message object
+   * which holds source information about where the exception
+   * occurred.  True by default.
+   */
+  void SetCaptureMessage(bool value);
+
+  /**
+   * There are cases when the raw address of C++ TryCatch object cannot be
+   * used for comparisons with addresses into the JS stack. The cases are:
+   * 1) ARM, ARM64 and MIPS simulators which have separate JS stack.
+   * 2) Address sanitizer allocates local C++ object in the heap when
+   *    UseAfterReturn mode is enabled.
+   * This method returns address that can be used for comparisons with
+   * addresses into the JS stack. When neither simulator nor ASAN's
+   * UseAfterReturn is enabled, then the address returned will be the address
+   * of the C++ try catch handler itself.
+   */
+  static void* JSStackComparableAddress(v8::TryCatch* handler) {
+    if (handler == NULL) return NULL;
+    return handler->js_stack_comparable_address_;
+  }
+
+ private:
+  void ResetInternal();
+
+  // Make it hard to create heap-allocated TryCatch blocks.
+  TryCatch(const TryCatch&);
+  void operator=(const TryCatch&);
+  void* operator new(size_t size);
+  void operator delete(void*, size_t);
+
+  v8::internal::Isolate* isolate_;
+  v8::TryCatch* next_;
+  void* exception_;
+  void* message_obj_;
+  void* js_stack_comparable_address_;
+  bool is_verbose_ : 1;
+  bool can_continue_ : 1;
+  bool capture_message_ : 1;
+  bool rethrow_ : 1;
+  bool has_terminated_ : 1;
+
+  friend class v8::internal::Isolate;
+};
+
+
+// --- Context ---
+
+
+/**
+ * A container for extension names.
+ */
+class V8_EXPORT ExtensionConfiguration {
+ public:
+  ExtensionConfiguration() : name_count_(0), names_(NULL) { }
+  ExtensionConfiguration(int name_count, const char* names[])
+      : name_count_(name_count), names_(names) { }
+
+  const char** begin() const { return &names_[0]; }
+  const char** end()  const { return &names_[name_count_]; }
+
+ private:
+  const int name_count_;
+  const char** names_;
+};
+
+
+/**
+ * A sandboxed execution context with its own set of built-in objects
+ * and functions.
+ */
+class V8_EXPORT Context {
+ public:
+  /**
+   * Returns the global proxy object.
+   *
+   * Global proxy object is a thin wrapper whose prototype points to actual
+   * context's global object with the properties like Object, etc. This is done
+   * that way for security reasons (for more details see
+   * https://wiki.mozilla.org/Gecko:SplitWindow).
+   *
+   * Please note that changes to global proxy object prototype most probably
+   * would break VM---v8 expects only global object as a prototype of global
+   * proxy object.
+   */
+  Local<Object> Global();
+
+  /**
+   * Detaches the global object from its context before
+   * the global object can be reused to create a new context.
+   */
+  void DetachGlobal();
+
+  /**
+   * Creates a new context and returns a handle to the newly allocated
+   * context.
+   *
+   * \param isolate The isolate in which to create the context.
+   *
+   * \param extensions An optional extension configuration containing
+   * the extensions to be installed in the newly created context.
+   *
+   * \param global_template An optional object template from which the
+   * global object for the newly created context will be created.
+   *
+   * \param global_object An optional global object to be reused for
+   * the newly created context. This global object must have been
+   * created by a previous call to Context::New with the same global
+   * template. The state of the global object will be completely reset
+   * and only object identify will remain.
+   */
+  static Local<Context> New(
+      Isolate* isolate, ExtensionConfiguration* extensions = NULL,
+      Local<ObjectTemplate> global_template = Local<ObjectTemplate>(),
+      Local<Value> global_object = Local<Value>());
+
+  /**
+   * Sets the security token for the context.  To access an object in
+   * another context, the security tokens must match.
+   */
+  void SetSecurityToken(Local<Value> token);
+
+  /** Restores the security token to the default value. */
+  void UseDefaultSecurityToken();
+
+  /** Returns the security token of this context.*/
+  Local<Value> GetSecurityToken();
+
+  /**
+   * Enter this context.  After entering a context, all code compiled
+   * and run is compiled and run in this context.  If another context
+   * is already entered, this old context is saved so it can be
+   * restored when the new context is exited.
+   */
+  void Enter();
+
+  /**
+   * Exit this context.  Exiting the current context restores the
+   * context that was in place when entering the current context.
+   */
+  void Exit();
+
+  /** Returns an isolate associated with a current context. */
+  v8::Isolate* GetIsolate();
+
+  /**
+   * The field at kDebugIdIndex is reserved for V8 debugger implementation.
+   * The value is propagated to the scripts compiled in given Context and
+   * can be used for filtering scripts.
+   */
+  enum EmbedderDataFields { kDebugIdIndex = 0 };
+
+  /**
+   * Gets the embedder data with the given index, which must have been set by a
+   * previous call to SetEmbedderData with the same index. Note that index 0
+   * currently has a special meaning for Chrome's debugger.
+   */
+  V8_INLINE Local<Value> GetEmbedderData(int index);
+
+  /**
+   * Gets the binding object used by V8 extras. Extra natives get a reference
+   * to this object and can use it to "export" functionality by adding
+   * properties. Extra natives can also "import" functionality by accessing
+   * properties added by the embedder using the V8 API.
+   */
+  Local<Object> GetExtrasBindingObject();
+
+  /**
+   * Sets the embedder data with the given index, growing the data as
+   * needed. Note that index 0 currently has a special meaning for Chrome's
+   * debugger.
+   */
+  void SetEmbedderData(int index, Local<Value> value);
+
+  /**
+   * Gets a 2-byte-aligned native pointer from the embedder data with the given
+   * index, which must have bees set by a previous call to
+   * SetAlignedPointerInEmbedderData with the same index. Note that index 0
+   * currently has a special meaning for Chrome's debugger.
+   */
+  V8_INLINE void* GetAlignedPointerFromEmbedderData(int index);
+
+  /**
+   * Sets a 2-byte-aligned native pointer in the embedder data with the given
+   * index, growing the data as needed. Note that index 0 currently has a
+   * special meaning for Chrome's debugger.
+   */
+  void SetAlignedPointerInEmbedderData(int index, void* value);
+
+  /**
+   * Control whether code generation from strings is allowed. Calling
+   * this method with false will disable 'eval' and the 'Function'
+   * constructor for code running in this context. If 'eval' or the
+   * 'Function' constructor are used an exception will be thrown.
+   *
+   * If code generation from strings is not allowed the
+   * V8::AllowCodeGenerationFromStrings callback will be invoked if
+   * set before blocking the call to 'eval' or the 'Function'
+   * constructor. If that callback returns true, the call will be
+   * allowed, otherwise an exception will be thrown. If no callback is
+   * set an exception will be thrown.
+   */
+  void AllowCodeGenerationFromStrings(bool allow);
+
+  /**
+   * Returns true if code generation from strings is allowed for the context.
+   * For more details see AllowCodeGenerationFromStrings(bool) documentation.
+   */
+  bool IsCodeGenerationFromStringsAllowed();
+
+  /**
+   * Sets the error description for the exception that is thrown when
+   * code generation from strings is not allowed and 'eval' or the 'Function'
+   * constructor are called.
+   */
+  void SetErrorMessageForCodeGenerationFromStrings(Local<String> message);
+
+  /**
+   * Estimate the memory in bytes retained by this context.
+   */
+  size_t EstimatedSize();
+
+  /**
+   * Stack-allocated class which sets the execution context for all
+   * operations executed within a local scope.
+   */
+  class Scope {
+   public:
+    explicit V8_INLINE Scope(Local<Context> context) : context_(context) {
+      context_->Enter();
+    }
+    V8_INLINE ~Scope() { context_->Exit(); }
+
+   private:
+    Local<Context> context_;
+  };
+
+ private:
+  friend class Value;
+  friend class Script;
+  friend class Object;
+  friend class Function;
+
+  Local<Value> SlowGetEmbedderData(int index);
+  void* SlowGetAlignedPointerFromEmbedderData(int index);
+};
+
+
+/**
+ * Multiple threads in V8 are allowed, but only one thread at a time is allowed
+ * to use any given V8 isolate, see the comments in the Isolate class. The
+ * definition of 'using a V8 isolate' includes accessing handles or holding onto
+ * object pointers obtained from V8 handles while in the particular V8 isolate.
+ * It is up to the user of V8 to ensure, perhaps with locking, that this
+ * constraint is not violated. In addition to any other synchronization
+ * mechanism that may be used, the v8::Locker and v8::Unlocker classes must be
+ * used to signal thead switches to V8.
+ *
+ * v8::Locker is a scoped lock object. While it's active, i.e. between its
+ * construction and destruction, the current thread is allowed to use the locked
+ * isolate. V8 guarantees that an isolate can be locked by at most one thread at
+ * any time. In other words, the scope of a v8::Locker is a critical section.
+ *
+ * Sample usage:
+* \code
+ * ...
+ * {
+ *   v8::Locker locker(isolate);
+ *   v8::Isolate::Scope isolate_scope(isolate);
+ *   ...
+ *   // Code using V8 and isolate goes here.
+ *   ...
+ * } // Destructor called here
+ * \endcode
+ *
+ * If you wish to stop using V8 in a thread A you can do this either by
+ * destroying the v8::Locker object as above or by constructing a v8::Unlocker
+ * object:
+ *
+ * \code
+ * {
+ *   isolate->Exit();
+ *   v8::Unlocker unlocker(isolate);
+ *   ...
+ *   // Code not using V8 goes here while V8 can run in another thread.
+ *   ...
+ * } // Destructor called here.
+ * isolate->Enter();
+ * \endcode
+ *
+ * The Unlocker object is intended for use in a long-running callback from V8,
+ * where you want to release the V8 lock for other threads to use.
+ *
+ * The v8::Locker is a recursive lock, i.e. you can lock more than once in a
+ * given thread. This can be useful if you have code that can be called either
+ * from code that holds the lock or from code that does not. The Unlocker is
+ * not recursive so you can not have several Unlockers on the stack at once, and
+ * you can not use an Unlocker in a thread that is not inside a Locker's scope.
+ *
+ * An unlocker will unlock several lockers if it has to and reinstate the
+ * correct depth of locking on its destruction, e.g.:
+ *
+ * \code
+ * // V8 not locked.
+ * {
+ *   v8::Locker locker(isolate);
+ *   Isolate::Scope isolate_scope(isolate);
+ *   // V8 locked.
+ *   {
+ *     v8::Locker another_locker(isolate);
+ *     // V8 still locked (2 levels).
+ *     {
+ *       isolate->Exit();
+ *       v8::Unlocker unlocker(isolate);
+ *       // V8 not locked.
+ *     }
+ *     isolate->Enter();
+ *     // V8 locked again (2 levels).
+ *   }
+ *   // V8 still locked (1 level).
+ * }
+ * // V8 Now no longer locked.
+ * \endcode
+ */
+class V8_EXPORT Unlocker {
+ public:
+  /**
+   * Initialize Unlocker for a given Isolate.
+   */
+  V8_INLINE explicit Unlocker(Isolate* isolate) { Initialize(isolate); }
+
+  ~Unlocker();
+ private:
+  void Initialize(Isolate* isolate);
+
+  internal::Isolate* isolate_;
+};
+
+
+class V8_EXPORT Locker {
+ public:
+  /**
+   * Initialize Locker for a given Isolate.
+   */
+  V8_INLINE explicit Locker(Isolate* isolate) { Initialize(isolate); }
+
+  ~Locker();
+
+  /**
+   * Returns whether or not the locker for a given isolate, is locked by the
+   * current thread.
+   */
+  static bool IsLocked(Isolate* isolate);
+
+  /**
+   * Returns whether v8::Locker is being used by this V8 instance.
+   */
+  static bool IsActive();
+
+ private:
+  void Initialize(Isolate* isolate);
+
+  bool has_lock_;
+  bool top_level_;
+  internal::Isolate* isolate_;
+
+  // Disallow copying and assigning.
+  Locker(const Locker&);
+  void operator=(const Locker&);
+};
+
+
+// --- Implementation ---
+
+
+namespace internal {
+
+const int kApiPointerSize = sizeof(void*);  // NOLINT
+const int kApiIntSize = sizeof(int);  // NOLINT
+const int kApiInt64Size = sizeof(int64_t);  // NOLINT
+
+// Tag information for HeapObject.
+const int kHeapObjectTag = 1;
+const int kHeapObjectTagSize = 2;
+const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1;
+
+// Tag information for Smi.
+const int kSmiTag = 0;
+const int kSmiTagSize = 1;
+const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
+
+template <size_t ptr_size> struct SmiTagging;
+
+template<int kSmiShiftSize>
+V8_INLINE internal::Object* IntToSmi(int value) {
+  int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
+  uintptr_t tagged_value =
+      (static_cast<uintptr_t>(value) << smi_shift_bits) | kSmiTag;
+  return reinterpret_cast<internal::Object*>(tagged_value);
+}
+
+// Smi constants for 32-bit systems.
+template <> struct SmiTagging<4> {
+  enum { kSmiShiftSize = 0, kSmiValueSize = 31 };
+  static int SmiShiftSize() { return kSmiShiftSize; }
+  static int SmiValueSize() { return kSmiValueSize; }
+  V8_INLINE static int SmiToInt(const internal::Object* value) {
+    int shift_bits = kSmiTagSize + kSmiShiftSize;
+    // Throw away top 32 bits and shift down (requires >> to be sign extending).
+    return static_cast<int>(reinterpret_cast<intptr_t>(value)) >> shift_bits;
+  }
+  V8_INLINE static internal::Object* IntToSmi(int value) {
+    return internal::IntToSmi<kSmiShiftSize>(value);
+  }
+  V8_INLINE static bool IsValidSmi(intptr_t value) {
+    // To be representable as an tagged small integer, the two
+    // most-significant bits of 'value' must be either 00 or 11 due to
+    // sign-extension. To check this we add 01 to the two
+    // most-significant bits, and check if the most-significant bit is 0
+    //
+    // CAUTION: The original code below:
+    // bool result = ((value + 0x40000000) & 0x80000000) == 0;
+    // may lead to incorrect results according to the C language spec, and
+    // in fact doesn't work correctly with gcc4.1.1 in some cases: The
+    // compiler may produce undefined results in case of signed integer
+    // overflow. The computation must be done w/ unsigned ints.
+    return static_cast<uintptr_t>(value + 0x40000000U) < 0x80000000U;
+  }
+};
+
+// Smi constants for 64-bit systems.
+template <> struct SmiTagging<8> {
+  enum { kSmiShiftSize = 31, kSmiValueSize = 32 };
+  static int SmiShiftSize() { return kSmiShiftSize; }
+  static int SmiValueSize() { return kSmiValueSize; }
+  V8_INLINE static int SmiToInt(const internal::Object* value) {
+    int shift_bits = kSmiTagSize + kSmiShiftSize;
+    // Shift down and throw away top 32 bits.
+    return static_cast<int>(reinterpret_cast<intptr_t>(value) >> shift_bits);
+  }
+  V8_INLINE static internal::Object* IntToSmi(int value) {
+    return internal::IntToSmi<kSmiShiftSize>(value);
+  }
+  V8_INLINE static bool IsValidSmi(intptr_t value) {
+    // To be representable as a long smi, the value must be a 32-bit integer.
+    return (value == static_cast<int32_t>(value));
+  }
+};
+
+typedef SmiTagging<kApiPointerSize> PlatformSmiTagging;
+const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
+const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
+V8_INLINE static bool SmiValuesAre31Bits() { return kSmiValueSize == 31; }
+V8_INLINE static bool SmiValuesAre32Bits() { return kSmiValueSize == 32; }
+
+/**
+ * This class exports constants and functionality from within v8 that
+ * is necessary to implement inline functions in the v8 api.  Don't
+ * depend on functions and constants defined here.
+ */
+class Internals {
+ public:
+  // These values match non-compiler-dependent values defined within
+  // the implementation of v8.
+  static const int kHeapObjectMapOffset = 0;
+  static const int kMapInstanceTypeAndBitFieldOffset =
+      1 * kApiPointerSize + kApiIntSize;
+  static const int kStringResourceOffset = 3 * kApiPointerSize;
+
+  static const int kOddballKindOffset = 4 * kApiPointerSize;
+  static const int kForeignAddressOffset = kApiPointerSize;
+  static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
+  static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
+  static const int kContextHeaderSize = 2 * kApiPointerSize;
+  static const int kContextEmbedderDataIndex = 5;
+  static const int kFullStringRepresentationMask = 0x07;
+  static const int kStringEncodingMask = 0x4;
+  static const int kExternalTwoByteRepresentationTag = 0x02;
+  static const int kExternalOneByteRepresentationTag = 0x06;
+
+  static const int kIsolateEmbedderDataOffset = 0 * kApiPointerSize;
+  static const int kAmountOfExternalAllocatedMemoryOffset =
+      4 * kApiPointerSize;
+  static const int kAmountOfExternalAllocatedMemoryAtLastGlobalGCOffset =
+      kAmountOfExternalAllocatedMemoryOffset + kApiInt64Size;
+  static const int kIsolateRootsOffset =
+      kAmountOfExternalAllocatedMemoryAtLastGlobalGCOffset + kApiInt64Size +
+      kApiPointerSize;
+  static const int kUndefinedValueRootIndex = 5;
+  static const int kNullValueRootIndex = 7;
+  static const int kTrueValueRootIndex = 8;
+  static const int kFalseValueRootIndex = 9;
+  static const int kEmptyStringRootIndex = 10;
+
+  // The external allocation limit should be below 256 MB on all architectures
+  // to avoid that resource-constrained embedders run low on memory.
+  static const int kExternalAllocationLimit = 192 * 1024 * 1024;
+
+  static const int kNodeClassIdOffset = 1 * kApiPointerSize;
+  static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3;
+  static const int kNodeStateMask = 0x7;
+  static const int kNodeStateIsWeakValue = 2;
+  static const int kNodeStateIsPendingValue = 3;
+  static const int kNodeStateIsNearDeathValue = 4;
+  static const int kNodeIsIndependentShift = 3;
+  static const int kNodeIsPartiallyDependentShift = 4;
+  static const int kNodeIsActiveShift = 4;
+
+  static const int kJSObjectType = 0xb7;
+  static const int kFirstNonstringType = 0x80;
+  static const int kOddballType = 0x83;
+  static const int kForeignType = 0x87;
+
+  static const int kUndefinedOddballKind = 5;
+  static const int kNullOddballKind = 3;
+
+  static const uint32_t kNumIsolateDataSlots = 4;
+
+  V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate);
+  V8_INLINE static void CheckInitialized(v8::Isolate* isolate) {
+#ifdef V8_ENABLE_CHECKS
+    CheckInitializedImpl(isolate);
+#endif
+  }
+
+  V8_INLINE static bool HasHeapObjectTag(const internal::Object* value) {
+    return ((reinterpret_cast<intptr_t>(value) & kHeapObjectTagMask) ==
+            kHeapObjectTag);
+  }
+
+  V8_INLINE static int SmiValue(const internal::Object* value) {
+    return PlatformSmiTagging::SmiToInt(value);
+  }
+
+  V8_INLINE static internal::Object* IntToSmi(int value) {
+    return PlatformSmiTagging::IntToSmi(value);
+  }
+
+  V8_INLINE static bool IsValidSmi(intptr_t value) {
+    return PlatformSmiTagging::IsValidSmi(value);
+  }
+
+  V8_INLINE static int GetInstanceType(const internal::Object* obj) {
+    typedef internal::Object O;
+    O* map = ReadField<O*>(obj, kHeapObjectMapOffset);
+    // Map::InstanceType is defined so that it will always be loaded into
+    // the LS 8 bits of one 16-bit word, regardless of endianess.
+    return ReadField<uint16_t>(map, kMapInstanceTypeAndBitFieldOffset) & 0xff;
+  }
+
+  V8_INLINE static int GetOddballKind(const internal::Object* obj) {
+    typedef internal::Object O;
+    return SmiValue(ReadField<O*>(obj, kOddballKindOffset));
+  }
+
+  V8_INLINE static bool IsExternalTwoByteString(int instance_type) {
+    int representation = (instance_type & kFullStringRepresentationMask);
+    return representation == kExternalTwoByteRepresentationTag;
+  }
+
+  V8_INLINE static uint8_t GetNodeFlag(internal::Object** obj, int shift) {
+      uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+      return *addr & static_cast<uint8_t>(1U << shift);
+  }
+
+  V8_INLINE static void UpdateNodeFlag(internal::Object** obj,
+                                       bool value, int shift) {
+      uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+      uint8_t mask = static_cast<uint8_t>(1U << shift);
+      *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift));
+  }
+
+  V8_INLINE static uint8_t GetNodeState(internal::Object** obj) {
+    uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+    return *addr & kNodeStateMask;
+  }
+
+  V8_INLINE static void UpdateNodeState(internal::Object** obj,
+                                        uint8_t value) {
+    uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+    *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value);
+  }
+
+  V8_INLINE static void SetEmbedderData(v8::Isolate* isolate,
+                                        uint32_t slot,
+                                        void* data) {
+    uint8_t *addr = reinterpret_cast<uint8_t *>(isolate) +
+                    kIsolateEmbedderDataOffset + slot * kApiPointerSize;
+    *reinterpret_cast<void**>(addr) = data;
+  }
+
+  V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate,
+                                         uint32_t slot) {
+    const uint8_t* addr = reinterpret_cast<const uint8_t*>(isolate) +
+        kIsolateEmbedderDataOffset + slot * kApiPointerSize;
+    return *reinterpret_cast<void* const*>(addr);
+  }
+
+  V8_INLINE static internal::Object** GetRoot(v8::Isolate* isolate,
+                                              int index) {
+    uint8_t* addr = reinterpret_cast<uint8_t*>(isolate) + kIsolateRootsOffset;
+    return reinterpret_cast<internal::Object**>(addr + index * kApiPointerSize);
+  }
+
+  template <typename T>
+  V8_INLINE static T ReadField(const internal::Object* ptr, int offset) {
+    const uint8_t* addr =
+        reinterpret_cast<const uint8_t*>(ptr) + offset - kHeapObjectTag;
+    return *reinterpret_cast<const T*>(addr);
+  }
+
+  template <typename T>
+  V8_INLINE static T ReadEmbedderData(const v8::Context* context, int index) {
+    typedef internal::Object O;
+    typedef internal::Internals I;
+    O* ctx = *reinterpret_cast<O* const*>(context);
+    int embedder_data_offset = I::kContextHeaderSize +
+        (internal::kApiPointerSize * I::kContextEmbedderDataIndex);
+    O* embedder_data = I::ReadField<O*>(ctx, embedder_data_offset);
+    int value_offset =
+        I::kFixedArrayHeaderSize + (internal::kApiPointerSize * index);
+    return I::ReadField<T>(embedder_data, value_offset);
+  }
+};
+
+}  // namespace internal
+
+
+template <class T>
+Local<T> Local<T>::New(Isolate* isolate, Local<T> that) {
+  return New(isolate, that.val_);
+}
+
+template <class T>
+Local<T> Local<T>::New(Isolate* isolate, const PersistentBase<T>& that) {
+  return New(isolate, that.val_);
+}
+
+
+template <class T>
+Local<T> Local<T>::New(Isolate* isolate, T* that) {
+  if (that == NULL) return Local<T>();
+  T* that_ptr = that;
+  internal::Object** p = reinterpret_cast<internal::Object**>(that_ptr);
+  return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
+      reinterpret_cast<internal::Isolate*>(isolate), *p)));
+}
+
+
+template<class T>
+template<class S>
+void Eternal<T>::Set(Isolate* isolate, Local<S> handle) {
+  TYPE_CHECK(T, S);
+  V8::Eternalize(isolate, reinterpret_cast<Value*>(*handle), &this->index_);
+}
+
+
+template<class T>
+Local<T> Eternal<T>::Get(Isolate* isolate) {
+  return Local<T>(reinterpret_cast<T*>(*V8::GetEternal(isolate, index_)));
+}
+
+
+template <class T>
+Local<T> MaybeLocal<T>::ToLocalChecked() {
+  if (V8_UNLIKELY(val_ == nullptr)) V8::ToLocalEmpty();
+  return Local<T>(val_);
+}
+
+
+template <class T>
+void* WeakCallbackInfo<T>::GetInternalField(int index) const {
+#ifdef V8_ENABLE_CHECKS
+  if (index < 0 || index >= kInternalFieldsInWeakCallback) {
+    V8::InternalFieldOutOfBounds(index);
+  }
+#endif
+  return internal_fields_[index];
+}
+
+
+template <class T>
+T* PersistentBase<T>::New(Isolate* isolate, T* that) {
+  if (that == NULL) return NULL;
+  internal::Object** p = reinterpret_cast<internal::Object**>(that);
+  return reinterpret_cast<T*>(
+      V8::GlobalizeReference(reinterpret_cast<internal::Isolate*>(isolate),
+                             p));
+}
+
+
+template <class T, class M>
+template <class S, class M2>
+void Persistent<T, M>::Copy(const Persistent<S, M2>& that) {
+  TYPE_CHECK(T, S);
+  this->Reset();
+  if (that.IsEmpty()) return;
+  internal::Object** p = reinterpret_cast<internal::Object**>(that.val_);
+  this->val_ = reinterpret_cast<T*>(V8::CopyPersistent(p));
+  M::Copy(that, this);
+}
+
+
+template <class T>
+bool PersistentBase<T>::IsIndependent() const {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return false;
+  return I::GetNodeFlag(reinterpret_cast<internal::Object**>(this->val_),
+                        I::kNodeIsIndependentShift);
+}
+
+
+template <class T>
+bool PersistentBase<T>::IsNearDeath() const {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return false;
+  uint8_t node_state =
+      I::GetNodeState(reinterpret_cast<internal::Object**>(this->val_));
+  return node_state == I::kNodeStateIsNearDeathValue ||
+      node_state == I::kNodeStateIsPendingValue;
+}
+
+
+template <class T>
+bool PersistentBase<T>::IsWeak() const {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return false;
+  return I::GetNodeState(reinterpret_cast<internal::Object**>(this->val_)) ==
+      I::kNodeStateIsWeakValue;
+}
+
+
+template <class T>
+void PersistentBase<T>::Reset() {
+  if (this->IsEmpty()) return;
+  V8::DisposeGlobal(reinterpret_cast<internal::Object**>(this->val_));
+  val_ = 0;
+}
+
+
+template <class T>
+template <class S>
+void PersistentBase<T>::Reset(Isolate* isolate, const Local<S>& other) {
+  TYPE_CHECK(T, S);
+  Reset();
+  if (other.IsEmpty()) return;
+  this->val_ = New(isolate, other.val_);
+}
+
+
+template <class T>
+template <class S>
+void PersistentBase<T>::Reset(Isolate* isolate,
+                              const PersistentBase<S>& other) {
+  TYPE_CHECK(T, S);
+  Reset();
+  if (other.IsEmpty()) return;
+  this->val_ = New(isolate, other.val_);
+}
+
+
+template <class T>
+template <typename S, typename P>
+void PersistentBase<T>::SetWeak(
+    P* parameter,
+    typename WeakCallbackData<S, P>::Callback callback) {
+  TYPE_CHECK(S, T);
+  typedef typename WeakCallbackData<Value, void>::Callback Callback;
+  V8::MakeWeak(reinterpret_cast<internal::Object**>(this->val_), parameter,
+               reinterpret_cast<Callback>(callback));
+}
+
+
+template <class T>
+template <typename P>
+void PersistentBase<T>::SetWeak(
+    P* parameter,
+    typename WeakCallbackData<T, P>::Callback callback) {
+  SetWeak<T, P>(parameter, callback);
+}
+
+
+template <class T>
+template <typename P>
+void PersistentBase<T>::SetPhantom(
+    P* parameter, typename WeakCallbackInfo<P>::Callback callback,
+    int internal_field_index1, int internal_field_index2) {
+  typedef typename WeakCallbackInfo<void>::Callback Callback;
+  V8::MakeWeak(reinterpret_cast<internal::Object**>(this->val_), parameter,
+               internal_field_index1, internal_field_index2,
+               reinterpret_cast<Callback>(callback));
+}
+
+
+template <class T>
+template <typename P>
+V8_INLINE void PersistentBase<T>::SetWeak(
+    P* parameter, typename WeakCallbackInfo<P>::Callback callback,
+    WeakCallbackType type) {
+  typedef typename WeakCallbackInfo<void>::Callback Callback;
+  V8::MakeWeak(reinterpret_cast<internal::Object**>(this->val_), parameter,
+               reinterpret_cast<Callback>(callback), type);
+}
+
+
+template <class T>
+template <typename P>
+P* PersistentBase<T>::ClearWeak() {
+  return reinterpret_cast<P*>(
+    V8::ClearWeak(reinterpret_cast<internal::Object**>(this->val_)));
+}
+
+
+template <class T>
+void PersistentBase<T>::MarkIndependent() {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return;
+  I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_),
+                    true,
+                    I::kNodeIsIndependentShift);
+}
+
+
+template <class T>
+void PersistentBase<T>::MarkPartiallyDependent() {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return;
+  I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_),
+                    true,
+                    I::kNodeIsPartiallyDependentShift);
+}
+
+
+template <class T>
+void PersistentBase<T>::MarkActive() {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return;
+  I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_), true,
+                    I::kNodeIsActiveShift);
+}
+
+
+template <class T>
+void PersistentBase<T>::SetWrapperClassId(uint16_t class_id) {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return;
+  internal::Object** obj = reinterpret_cast<internal::Object**>(this->val_);
+  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
+  *reinterpret_cast<uint16_t*>(addr) = class_id;
+}
+
+
+template <class T>
+uint16_t PersistentBase<T>::WrapperClassId() const {
+  typedef internal::Internals I;
+  if (this->IsEmpty()) return 0;
+  internal::Object** obj = reinterpret_cast<internal::Object**>(this->val_);
+  uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
+  return *reinterpret_cast<uint16_t*>(addr);
+}
+
+
+template<typename T>
+ReturnValue<T>::ReturnValue(internal::Object** slot) : value_(slot) {}
+
+template<typename T>
+template<typename S>
+void ReturnValue<T>::Set(const Persistent<S>& handle) {
+  TYPE_CHECK(T, S);
+  if (V8_UNLIKELY(handle.IsEmpty())) {
+    *value_ = GetDefaultValue();
+  } else {
+    *value_ = *reinterpret_cast<internal::Object**>(*handle);
+  }
+}
+
+template <typename T>
+template <typename S>
+void ReturnValue<T>::Set(const Global<S>& handle) {
+  TYPE_CHECK(T, S);
+  if (V8_UNLIKELY(handle.IsEmpty())) {
+    *value_ = GetDefaultValue();
+  } else {
+    *value_ = *reinterpret_cast<internal::Object**>(*handle);
+  }
+}
+
+template <typename T>
+template <typename S>
+void ReturnValue<T>::Set(const Local<S> handle) {
+  TYPE_CHECK(T, S);
+  if (V8_UNLIKELY(handle.IsEmpty())) {
+    *value_ = GetDefaultValue();
+  } else {
+    *value_ = *reinterpret_cast<internal::Object**>(*handle);
+  }
+}
+
+template<typename T>
+void ReturnValue<T>::Set(double i) {
+  TYPE_CHECK(T, Number);
+  Set(Number::New(GetIsolate(), i));
+}
+
+template<typename T>
+void ReturnValue<T>::Set(int32_t i) {
+  TYPE_CHECK(T, Integer);
+  typedef internal::Internals I;
+  if (V8_LIKELY(I::IsValidSmi(i))) {
+    *value_ = I::IntToSmi(i);
+    return;
+  }
+  Set(Integer::New(GetIsolate(), i));
+}
+
+template<typename T>
+void ReturnValue<T>::Set(uint32_t i) {
+  TYPE_CHECK(T, Integer);
+  // Can't simply use INT32_MAX here for whatever reason.
+  bool fits_into_int32_t = (i & (1U << 31)) == 0;
+  if (V8_LIKELY(fits_into_int32_t)) {
+    Set(static_cast<int32_t>(i));
+    return;
+  }
+  Set(Integer::NewFromUnsigned(GetIsolate(), i));
+}
+
+template<typename T>
+void ReturnValue<T>::Set(bool value) {
+  TYPE_CHECK(T, Boolean);
+  typedef internal::Internals I;
+  int root_index;
+  if (value) {
+    root_index = I::kTrueValueRootIndex;
+  } else {
+    root_index = I::kFalseValueRootIndex;
+  }
+  *value_ = *I::GetRoot(GetIsolate(), root_index);
+}
+
+template<typename T>
+void ReturnValue<T>::SetNull() {
+  TYPE_CHECK(T, Primitive);
+  typedef internal::Internals I;
+  *value_ = *I::GetRoot(GetIsolate(), I::kNullValueRootIndex);
+}
+
+template<typename T>
+void ReturnValue<T>::SetUndefined() {
+  TYPE_CHECK(T, Primitive);
+  typedef internal::Internals I;
+  *value_ = *I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex);
+}
+
+template<typename T>
+void ReturnValue<T>::SetEmptyString() {
+  TYPE_CHECK(T, String);
+  typedef internal::Internals I;
+  *value_ = *I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex);
+}
+
+template<typename T>
+Isolate* ReturnValue<T>::GetIsolate() {
+  // Isolate is always the pointer below the default value on the stack.
+  return *reinterpret_cast<Isolate**>(&value_[-2]);
+}
+
+template<typename T>
+template<typename S>
+void ReturnValue<T>::Set(S* whatever) {
+  // Uncompilable to prevent inadvertent misuse.
+  TYPE_CHECK(S*, Primitive);
+}
+
+template<typename T>
+internal::Object* ReturnValue<T>::GetDefaultValue() {
+  // Default value is always the pointer below value_ on the stack.
+  return value_[-1];
+}
+
+
+template<typename T>
+FunctionCallbackInfo<T>::FunctionCallbackInfo(internal::Object** implicit_args,
+                                              internal::Object** values,
+                                              int length,
+                                              bool is_construct_call)
+    : implicit_args_(implicit_args),
+      values_(values),
+      length_(length),
+      is_construct_call_(is_construct_call) { }
+
+
+template<typename T>
+Local<Value> FunctionCallbackInfo<T>::operator[](int i) const {
+  if (i < 0 || length_ <= i) return Local<Value>(*Undefined(GetIsolate()));
+  return Local<Value>(reinterpret_cast<Value*>(values_ - i));
+}
+
+
+template<typename T>
+Local<Function> FunctionCallbackInfo<T>::Callee() const {
+  return Local<Function>(reinterpret_cast<Function*>(
+      &implicit_args_[kCalleeIndex]));
+}
+
+
+template<typename T>
+Local<Object> FunctionCallbackInfo<T>::This() const {
+  return Local<Object>(reinterpret_cast<Object*>(values_ + 1));
+}
+
+
+template<typename T>
+Local<Object> FunctionCallbackInfo<T>::Holder() const {
+  return Local<Object>(reinterpret_cast<Object*>(
+      &implicit_args_[kHolderIndex]));
+}
+
+
+template<typename T>
+Local<Value> FunctionCallbackInfo<T>::Data() const {
+  return Local<Value>(reinterpret_cast<Value*>(&implicit_args_[kDataIndex]));
+}
+
+
+template<typename T>
+Isolate* FunctionCallbackInfo<T>::GetIsolate() const {
+  return *reinterpret_cast<Isolate**>(&implicit_args_[kIsolateIndex]);
+}
+
+
+template<typename T>
+ReturnValue<T> FunctionCallbackInfo<T>::GetReturnValue() const {
+  return ReturnValue<T>(&implicit_args_[kReturnValueIndex]);
+}
+
+
+template<typename T>
+bool FunctionCallbackInfo<T>::IsConstructCall() const {
+  return is_construct_call_ & 0x1;
+}
+
+
+template<typename T>
+int FunctionCallbackInfo<T>::Length() const {
+  return length_;
+}
+
+ScriptOrigin::ScriptOrigin(Local<Value> resource_name,
+                           Local<Integer> resource_line_offset,
+                           Local<Integer> resource_column_offset,
+                           Local<Boolean> resource_is_shared_cross_origin,
+                           Local<Integer> script_id,
+                           Local<Boolean> resource_is_embedder_debug_script,
+                           Local<Value> source_map_url,
+                           Local<Boolean> resource_is_opaque)
+    : resource_name_(resource_name),
+      resource_line_offset_(resource_line_offset),
+      resource_column_offset_(resource_column_offset),
+      options_(!resource_is_embedder_debug_script.IsEmpty() &&
+                   resource_is_embedder_debug_script->IsTrue(),
+               !resource_is_shared_cross_origin.IsEmpty() &&
+                   resource_is_shared_cross_origin->IsTrue(),
+               !resource_is_opaque.IsEmpty() && resource_is_opaque->IsTrue()),
+      script_id_(script_id),
+      source_map_url_(source_map_url) {}
+
+Local<Value> ScriptOrigin::ResourceName() const { return resource_name_; }
+
+
+Local<Integer> ScriptOrigin::ResourceLineOffset() const {
+  return resource_line_offset_;
+}
+
+
+Local<Integer> ScriptOrigin::ResourceColumnOffset() const {
+  return resource_column_offset_;
+}
+
+
+Local<Integer> ScriptOrigin::ScriptID() const { return script_id_; }
+
+
+Local<Value> ScriptOrigin::SourceMapUrl() const { return source_map_url_; }
+
+
+ScriptCompiler::Source::Source(Local<String> string, const ScriptOrigin& origin,
+                               CachedData* data)
+    : source_string(string),
+      resource_name(origin.ResourceName()),
+      resource_line_offset(origin.ResourceLineOffset()),
+      resource_column_offset(origin.ResourceColumnOffset()),
+      resource_options(origin.Options()),
+      source_map_url(origin.SourceMapUrl()),
+      cached_data(data) {}
+
+
+ScriptCompiler::Source::Source(Local<String> string,
+                               CachedData* data)
+    : source_string(string), cached_data(data) {}
+
+
+ScriptCompiler::Source::~Source() {
+  delete cached_data;
+}
+
+
+const ScriptCompiler::CachedData* ScriptCompiler::Source::GetCachedData()
+    const {
+  return cached_data;
+}
+
+
+Local<Boolean> Boolean::New(Isolate* isolate, bool value) {
+  return value ? True(isolate) : False(isolate);
+}
+
+
+void Template::Set(Isolate* isolate, const char* name, v8::Local<Data> value) {
+  Set(v8::String::NewFromUtf8(isolate, name, NewStringType::kNormal)
+          .ToLocalChecked(),
+      value);
+}
+
+
+Local<Value> Object::GetInternalField(int index) {
+#ifndef V8_ENABLE_CHECKS
+  typedef internal::Object O;
+  typedef internal::HeapObject HO;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O**>(this);
+  // Fast path: If the object is a plain JSObject, which is the common case, we
+  // know where to find the internal fields and can return the value directly.
+  if (I::GetInstanceType(obj) == I::kJSObjectType) {
+    int offset = I::kJSObjectHeaderSize + (internal::kApiPointerSize * index);
+    O* value = I::ReadField<O*>(obj, offset);
+    O** result = HandleScope::CreateHandle(reinterpret_cast<HO*>(obj), value);
+    return Local<Value>(reinterpret_cast<Value*>(result));
+  }
+#endif
+  return SlowGetInternalField(index);
+}
+
+
+void* Object::GetAlignedPointerFromInternalField(int index) {
+#ifndef V8_ENABLE_CHECKS
+  typedef internal::Object O;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O**>(this);
+  // Fast path: If the object is a plain JSObject, which is the common case, we
+  // know where to find the internal fields and can return the value directly.
+  if (V8_LIKELY(I::GetInstanceType(obj) == I::kJSObjectType)) {
+    int offset = I::kJSObjectHeaderSize + (internal::kApiPointerSize * index);
+    return I::ReadField<void*>(obj, offset);
+  }
+#endif
+  return SlowGetAlignedPointerFromInternalField(index);
+}
+
+
+String* String::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<String*>(value);
+}
+
+
+Local<String> String::Empty(Isolate* isolate) {
+  typedef internal::Object* S;
+  typedef internal::Internals I;
+  I::CheckInitialized(isolate);
+  S* slot = I::GetRoot(isolate, I::kEmptyStringRootIndex);
+  return Local<String>(reinterpret_cast<String*>(slot));
+}
+
+
+String::ExternalStringResource* String::GetExternalStringResource() const {
+  typedef internal::Object O;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O* const*>(this);
+  String::ExternalStringResource* result;
+  if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) {
+    void* value = I::ReadField<void*>(obj, I::kStringResourceOffset);
+    result = reinterpret_cast<String::ExternalStringResource*>(value);
+  } else {
+    result = NULL;
+  }
+#ifdef V8_ENABLE_CHECKS
+  VerifyExternalStringResource(result);
+#endif
+  return result;
+}
+
+
+String::ExternalStringResourceBase* String::GetExternalStringResourceBase(
+    String::Encoding* encoding_out) const {
+  typedef internal::Object O;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O* const*>(this);
+  int type = I::GetInstanceType(obj) & I::kFullStringRepresentationMask;
+  *encoding_out = static_cast<Encoding>(type & I::kStringEncodingMask);
+  ExternalStringResourceBase* resource = NULL;
+  if (type == I::kExternalOneByteRepresentationTag ||
+      type == I::kExternalTwoByteRepresentationTag) {
+    void* value = I::ReadField<void*>(obj, I::kStringResourceOffset);
+    resource = static_cast<ExternalStringResourceBase*>(value);
+  }
+#ifdef V8_ENABLE_CHECKS
+    VerifyExternalStringResourceBase(resource, *encoding_out);
+#endif
+  return resource;
+}
+
+
+bool Value::IsUndefined() const {
+#ifdef V8_ENABLE_CHECKS
+  return FullIsUndefined();
+#else
+  return QuickIsUndefined();
+#endif
+}
+
+bool Value::QuickIsUndefined() const {
+  typedef internal::Object O;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O* const*>(this);
+  if (!I::HasHeapObjectTag(obj)) return false;
+  if (I::GetInstanceType(obj) != I::kOddballType) return false;
+  return (I::GetOddballKind(obj) == I::kUndefinedOddballKind);
+}
+
+
+bool Value::IsNull() const {
+#ifdef V8_ENABLE_CHECKS
+  return FullIsNull();
+#else
+  return QuickIsNull();
+#endif
+}
+
+bool Value::QuickIsNull() const {
+  typedef internal::Object O;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O* const*>(this);
+  if (!I::HasHeapObjectTag(obj)) return false;
+  if (I::GetInstanceType(obj) != I::kOddballType) return false;
+  return (I::GetOddballKind(obj) == I::kNullOddballKind);
+}
+
+
+bool Value::IsString() const {
+#ifdef V8_ENABLE_CHECKS
+  return FullIsString();
+#else
+  return QuickIsString();
+#endif
+}
+
+bool Value::QuickIsString() const {
+  typedef internal::Object O;
+  typedef internal::Internals I;
+  O* obj = *reinterpret_cast<O* const*>(this);
+  if (!I::HasHeapObjectTag(obj)) return false;
+  return (I::GetInstanceType(obj) < I::kFirstNonstringType);
+}
+
+
+template <class T> Value* Value::Cast(T* value) {
+  return static_cast<Value*>(value);
+}
+
+
+Local<Boolean> Value::ToBoolean() const {
+  return ToBoolean(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<Boolean>());
+}
+
+
+Local<Number> Value::ToNumber() const {
+  return ToNumber(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<Number>());
+}
+
+
+Local<String> Value::ToString() const {
+  return ToString(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<String>());
+}
+
+
+Local<String> Value::ToDetailString() const {
+  return ToDetailString(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<String>());
+}
+
+
+Local<Object> Value::ToObject() const {
+  return ToObject(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<Object>());
+}
+
+
+Local<Integer> Value::ToInteger() const {
+  return ToInteger(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<Integer>());
+}
+
+
+Local<Uint32> Value::ToUint32() const {
+  return ToUint32(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<Uint32>());
+}
+
+
+Local<Int32> Value::ToInt32() const {
+  return ToInt32(Isolate::GetCurrent()->GetCurrentContext())
+      .FromMaybe(Local<Int32>());
+}
+
+
+Boolean* Boolean::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Boolean*>(value);
+}
+
+
+Name* Name::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Name*>(value);
+}
+
+
+Symbol* Symbol::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Symbol*>(value);
+}
+
+
+Number* Number::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Number*>(value);
+}
+
+
+Integer* Integer::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Integer*>(value);
+}
+
+
+Int32* Int32::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Int32*>(value);
+}
+
+
+Uint32* Uint32::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Uint32*>(value);
+}
+
+
+Date* Date::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Date*>(value);
+}
+
+
+StringObject* StringObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<StringObject*>(value);
+}
+
+
+SymbolObject* SymbolObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<SymbolObject*>(value);
+}
+
+
+NumberObject* NumberObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<NumberObject*>(value);
+}
+
+
+BooleanObject* BooleanObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<BooleanObject*>(value);
+}
+
+
+RegExp* RegExp::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<RegExp*>(value);
+}
+
+
+Object* Object::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Object*>(value);
+}
+
+
+Array* Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Array*>(value);
+}
+
+
+Map* Map::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Map*>(value);
+}
+
+
+Set* Set::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Set*>(value);
+}
+
+
+Promise* Promise::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Promise*>(value);
+}
+
+
+Promise::Resolver* Promise::Resolver::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Promise::Resolver*>(value);
+}
+
+
+ArrayBuffer* ArrayBuffer::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<ArrayBuffer*>(value);
+}
+
+
+ArrayBufferView* ArrayBufferView::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<ArrayBufferView*>(value);
+}
+
+
+TypedArray* TypedArray::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<TypedArray*>(value);
+}
+
+
+Uint8Array* Uint8Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Uint8Array*>(value);
+}
+
+
+Int8Array* Int8Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Int8Array*>(value);
+}
+
+
+Uint16Array* Uint16Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Uint16Array*>(value);
+}
+
+
+Int16Array* Int16Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Int16Array*>(value);
+}
+
+
+Uint32Array* Uint32Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Uint32Array*>(value);
+}
+
+
+Int32Array* Int32Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Int32Array*>(value);
+}
+
+
+Float32Array* Float32Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Float32Array*>(value);
+}
+
+
+Float64Array* Float64Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Float64Array*>(value);
+}
+
+
+Uint8ClampedArray* Uint8ClampedArray::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Uint8ClampedArray*>(value);
+}
+
+
+DataView* DataView::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<DataView*>(value);
+}
+
+
+SharedArrayBuffer* SharedArrayBuffer::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<SharedArrayBuffer*>(value);
+}
+
+
+Function* Function::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<Function*>(value);
+}
+
+
+External* External::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+  CheckCast(value);
+#endif
+  return static_cast<External*>(value);
+}
+
+
+template<typename T>
+Isolate* PropertyCallbackInfo<T>::GetIsolate() const {
+  return *reinterpret_cast<Isolate**>(&args_[kIsolateIndex]);
+}
+
+
+template<typename T>
+Local<Value> PropertyCallbackInfo<T>::Data() const {
+  return Local<Value>(reinterpret_cast<Value*>(&args_[kDataIndex]));
+}
+
+
+template<typename T>
+Local<Object> PropertyCallbackInfo<T>::This() const {
+  return Local<Object>(reinterpret_cast<Object*>(&args_[kThisIndex]));
+}
+
+
+template<typename T>
+Local<Object> PropertyCallbackInfo<T>::Holder() const {
+  return Local<Object>(reinterpret_cast<Object*>(&args_[kHolderIndex]));
+}
+
+
+template<typename T>
+ReturnValue<T> PropertyCallbackInfo<T>::GetReturnValue() const {
+  return ReturnValue<T>(&args_[kReturnValueIndex]);
+}
+
+
+Local<Primitive> Undefined(Isolate* isolate) {
+  typedef internal::Object* S;
+  typedef internal::Internals I;
+  I::CheckInitialized(isolate);
+  S* slot = I::GetRoot(isolate, I::kUndefinedValueRootIndex);
+  return Local<Primitive>(reinterpret_cast<Primitive*>(slot));
+}
+
+
+Local<Primitive> Null(Isolate* isolate) {
+  typedef internal::Object* S;
+  typedef internal::Internals I;
+  I::CheckInitialized(isolate);
+  S* slot = I::GetRoot(isolate, I::kNullValueRootIndex);
+  return Local<Primitive>(reinterpret_cast<Primitive*>(slot));
+}
+
+
+Local<Boolean> True(Isolate* isolate) {
+  typedef internal::Object* S;
+  typedef internal::Internals I;
+  I::CheckInitialized(isolate);
+  S* slot = I::GetRoot(isolate, I::kTrueValueRootIndex);
+  return Local<Boolean>(reinterpret_cast<Boolean*>(slot));
+}
+
+
+Local<Boolean> False(Isolate* isolate) {
+  typedef internal::Object* S;
+  typedef internal::Internals I;
+  I::CheckInitialized(isolate);
+  S* slot = I::GetRoot(isolate, I::kFalseValueRootIndex);
+  return Local<Boolean>(reinterpret_cast<Boolean*>(slot));
+}
+
+
+void Isolate::SetData(uint32_t slot, void* data) {
+  typedef internal::Internals I;
+  I::SetEmbedderData(this, slot, data);
+}
+
+
+void* Isolate::GetData(uint32_t slot) {
+  typedef internal::Internals I;
+  return I::GetEmbedderData(this, slot);
+}
+
+
+uint32_t Isolate::GetNumberOfDataSlots() {
+  typedef internal::Internals I;
+  return I::kNumIsolateDataSlots;
+}
+
+
+int64_t Isolate::AdjustAmountOfExternalAllocatedMemory(
+    int64_t change_in_bytes) {
+  typedef internal::Internals I;
+  int64_t* amount_of_external_allocated_memory =
+      reinterpret_cast<int64_t*>(reinterpret_cast<uint8_t*>(this) +
+                                 I::kAmountOfExternalAllocatedMemoryOffset);
+  int64_t* amount_of_external_allocated_memory_at_last_global_gc =
+      reinterpret_cast<int64_t*>(
+          reinterpret_cast<uint8_t*>(this) +
+          I::kAmountOfExternalAllocatedMemoryAtLastGlobalGCOffset);
+  int64_t amount = *amount_of_external_allocated_memory + change_in_bytes;
+  if (change_in_bytes > 0 &&
+      amount - *amount_of_external_allocated_memory_at_last_global_gc >
+          I::kExternalAllocationLimit) {
+    ReportExternalAllocationLimitReached();
+  }
+  *amount_of_external_allocated_memory = amount;
+  return *amount_of_external_allocated_memory;
+}
+
+
+template<typename T>
+void Isolate::SetObjectGroupId(const Persistent<T>& object,
+                               UniqueId id) {
+  TYPE_CHECK(Value, T);
+  SetObjectGroupId(reinterpret_cast<v8::internal::Object**>(object.val_), id);
+}
+
+
+template<typename T>
+void Isolate::SetReferenceFromGroup(UniqueId id,
+                                    const Persistent<T>& object) {
+  TYPE_CHECK(Value, T);
+  SetReferenceFromGroup(id,
+                        reinterpret_cast<v8::internal::Object**>(object.val_));
+}
+
+
+template<typename T, typename S>
+void Isolate::SetReference(const Persistent<T>& parent,
+                           const Persistent<S>& child) {
+  TYPE_CHECK(Object, T);
+  TYPE_CHECK(Value, S);
+  SetReference(reinterpret_cast<v8::internal::Object**>(parent.val_),
+               reinterpret_cast<v8::internal::Object**>(child.val_));
+}
+
+
+Local<Value> Context::GetEmbedderData(int index) {
+#ifndef V8_ENABLE_CHECKS
+  typedef internal::Object O;
+  typedef internal::HeapObject HO;
+  typedef internal::Internals I;
+  HO* context = *reinterpret_cast<HO**>(this);
+  O** result =
+      HandleScope::CreateHandle(context, I::ReadEmbedderData<O*>(this, index));
+  return Local<Value>(reinterpret_cast<Value*>(result));
+#else
+  return SlowGetEmbedderData(index);
+#endif
+}
+
+
+void* Context::GetAlignedPointerFromEmbedderData(int index) {
+#ifndef V8_ENABLE_CHECKS
+  typedef internal::Internals I;
+  return I::ReadEmbedderData<void*>(this, index);
+#else
+  return SlowGetAlignedPointerFromEmbedderData(index);
+#endif
+}
+
+
+void V8::SetAllowCodeGenerationFromStringsCallback(
+    AllowCodeGenerationFromStringsCallback callback) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->SetAllowCodeGenerationFromStringsCallback(callback);
+}
+
+
+bool V8::IsDead() {
+  Isolate* isolate = Isolate::GetCurrent();
+  return isolate->IsDead();
+}
+
+
+bool V8::AddMessageListener(MessageCallback that, Local<Value> data) {
+  Isolate* isolate = Isolate::GetCurrent();
+  return isolate->AddMessageListener(that, data);
+}
+
+
+void V8::RemoveMessageListeners(MessageCallback that) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->RemoveMessageListeners(that);
+}
+
+
+void V8::SetFailedAccessCheckCallbackFunction(
+    FailedAccessCheckCallback callback) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->SetFailedAccessCheckCallbackFunction(callback);
+}
+
+
+void V8::SetCaptureStackTraceForUncaughtExceptions(
+    bool capture, int frame_limit, StackTrace::StackTraceOptions options) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->SetCaptureStackTraceForUncaughtExceptions(capture, frame_limit,
+                                                     options);
+}
+
+
+void V8::SetFatalErrorHandler(FatalErrorCallback callback) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->SetFatalErrorHandler(callback);
+}
+
+
+void V8::RemoveGCPrologueCallback(GCCallback callback) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->RemoveGCPrologueCallback(
+      reinterpret_cast<v8::Isolate::GCCallback>(callback));
+}
+
+
+void V8::RemoveGCEpilogueCallback(GCCallback callback) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->RemoveGCEpilogueCallback(
+      reinterpret_cast<v8::Isolate::GCCallback>(callback));
+}
+
+
+void V8::AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+                                     ObjectSpace space,
+                                     AllocationAction action) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->AddMemoryAllocationCallback(callback, space, action);
+}
+
+
+void V8::RemoveMemoryAllocationCallback(MemoryAllocationCallback callback) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->RemoveMemoryAllocationCallback(callback);
+}
+
+
+void V8::TerminateExecution(Isolate* isolate) { isolate->TerminateExecution(); }
+
+
+bool V8::IsExecutionTerminating(Isolate* isolate) {
+  if (isolate == NULL) {
+    isolate = Isolate::GetCurrent();
+  }
+  return isolate->IsExecutionTerminating();
+}
+
+
+void V8::CancelTerminateExecution(Isolate* isolate) {
+  isolate->CancelTerminateExecution();
+}
+
+
+void V8::VisitExternalResources(ExternalResourceVisitor* visitor) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->VisitExternalResources(visitor);
+}
+
+
+void V8::VisitHandlesWithClassIds(PersistentHandleVisitor* visitor) {
+  Isolate* isolate = Isolate::GetCurrent();
+  isolate->VisitHandlesWithClassIds(visitor);
+}
+
+
+void V8::VisitHandlesWithClassIds(Isolate* isolate,
+                                  PersistentHandleVisitor* visitor) {
+  isolate->VisitHandlesWithClassIds(visitor);
+}
+
+
+void V8::VisitHandlesForPartialDependence(Isolate* isolate,
+                                          PersistentHandleVisitor* visitor) {
+  isolate->VisitHandlesForPartialDependence(visitor);
+}
+
+/**
+ * \example shell.cc
+ * A simple shell that takes a list of expressions on the
+ * command-line and executes them.
+ */
+
+
+/**
+ * \example process.cc
+ */
+
+
+}  // namespace v8
+
+
+#undef TYPE_CHECK
+
+
+#endif  // V8_H_
diff --git a/Plugins/UnrealJS/ThirdParty/v8/include/v8config.h b/Plugins/UnrealJS/ThirdParty/v8/include/v8config.h
new file mode 100644
index 0000000..c88e1da
--- /dev/null
+++ b/Plugins/UnrealJS/ThirdParty/v8/include/v8config.h
@@ -0,0 +1,441 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8CONFIG_H_
+#define V8CONFIG_H_
+
+// clang-format off
+
+// Platform headers for feature detection below.
+#if defined(__ANDROID__)
+# include <sys/cdefs.h>
+#elif defined(__APPLE__)
+# include <TargetConditionals.h>
+#elif defined(__linux__)
+# include <features.h>
+#endif
+
+
+// This macro allows to test for the version of the GNU C library (or
+// a compatible C library that masquerades as glibc). It evaluates to
+// 0 if libc is not GNU libc or compatible.
+// Use like:
+//  #if V8_GLIBC_PREREQ(2, 3)
+//   ...
+//  #endif
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
+# define V8_GLIBC_PREREQ(major, minor)                                    \
+    ((__GLIBC__ * 100 + __GLIBC_MINOR__) >= ((major) * 100 + (minor)))
+#else
+# define V8_GLIBC_PREREQ(major, minor) 0
+#endif
+
+
+// This macro allows to test for the version of the GNU C++ compiler.
+// Note that this also applies to compilers that masquerade as GCC,
+// for example clang and the Intel C++ compiler for Linux.
+// Use like:
+//  #if V8_GNUC_PREREQ(4, 3, 1)
+//   ...
+//  #endif
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
+# define V8_GNUC_PREREQ(major, minor, patchlevel)                         \
+    ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >=   \
+     ((major) * 10000 + (minor) * 100 + (patchlevel)))
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define V8_GNUC_PREREQ(major, minor, patchlevel)      \
+    ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >=      \
+     ((major) * 10000 + (minor) * 100 + (patchlevel)))
+#else
+# define V8_GNUC_PREREQ(major, minor, patchlevel) 0
+#endif
+
+
+
+// -----------------------------------------------------------------------------
+// Operating system detection
+//
+//  V8_OS_ANDROID       - Android
+//  V8_OS_BSD           - BSDish (Mac OS X, Net/Free/Open/DragonFlyBSD)
+//  V8_OS_CYGWIN        - Cygwin
+//  V8_OS_DRAGONFLYBSD  - DragonFlyBSD
+//  V8_OS_FREEBSD       - FreeBSD
+//  V8_OS_LINUX         - Linux
+//  V8_OS_MACOSX        - Mac OS X
+//  V8_OS_NACL          - Native Client
+//  V8_OS_NETBSD        - NetBSD
+//  V8_OS_OPENBSD       - OpenBSD
+//  V8_OS_POSIX         - POSIX compatible (mostly everything except Windows)
+//  V8_OS_QNX           - QNX Neutrino
+//  V8_OS_SOLARIS       - Sun Solaris and OpenSolaris
+//  V8_OS_AIX           - AIX
+//  V8_OS_WIN           - Microsoft Windows
+
+#if defined(__ANDROID__)
+# define V8_OS_ANDROID 1
+# define V8_OS_LINUX 1
+# define V8_OS_POSIX 1
+#elif defined(__APPLE__)
+# define V8_OS_BSD 1
+# define V8_OS_MACOSX 1
+# define V8_OS_POSIX 1
+#elif defined(__native_client__)
+# define V8_OS_NACL 1
+# define V8_OS_POSIX 1
+#elif defined(__CYGWIN__)
+# define V8_OS_CYGWIN 1
+# define V8_OS_POSIX 1
+#elif defined(__linux__)
+# define V8_OS_LINUX 1
+# define V8_OS_POSIX 1
+#elif defined(__sun)
+# define V8_OS_POSIX 1
+# define V8_OS_SOLARIS 1
+#elif defined(_AIX)
+#define V8_OS_POSIX 1
+#define V8_OS_AIX 1
+#elif defined(__FreeBSD__)
+# define V8_OS_BSD 1
+# define V8_OS_FREEBSD 1
+# define V8_OS_POSIX 1
+#elif defined(__DragonFly__)
+# define V8_OS_BSD 1
+# define V8_OS_DRAGONFLYBSD 1
+# define V8_OS_POSIX 1
+#elif defined(__NetBSD__)
+# define V8_OS_BSD 1
+# define V8_OS_NETBSD 1
+# define V8_OS_POSIX 1
+#elif defined(__OpenBSD__)
+# define V8_OS_BSD 1
+# define V8_OS_OPENBSD 1
+# define V8_OS_POSIX 1
+#elif defined(__QNXNTO__)
+# define V8_OS_POSIX 1
+# define V8_OS_QNX 1
+#elif defined(_WIN32)
+# define V8_OS_WIN 1
+#endif
+
+
+// -----------------------------------------------------------------------------
+// C library detection
+//
+//  V8_LIBC_MSVCRT  - MSVC libc
+//  V8_LIBC_BIONIC  - Bionic libc
+//  V8_LIBC_BSD     - BSD libc derivate
+//  V8_LIBC_GLIBC   - GNU C library
+//  V8_LIBC_UCLIBC  - uClibc
+//
+// Note that testing for libc must be done using #if not #ifdef. For example,
+// to test for the GNU C library, use:
+//  #if V8_LIBC_GLIBC
+//   ...
+//  #endif
+
+#if defined (_MSC_VER)
+# define V8_LIBC_MSVCRT 1
+#elif defined(__BIONIC__)
+# define V8_LIBC_BIONIC 1
+# define V8_LIBC_BSD 1
+#elif defined(__UCLIBC__)
+// Must test for UCLIBC before GLIBC, as UCLIBC pretends to be GLIBC.
+# define V8_LIBC_UCLIBC 1
+#elif defined(__GLIBC__) || defined(__GNU_LIBRARY__)
+# define V8_LIBC_GLIBC 1
+#else
+# define V8_LIBC_BSD V8_OS_BSD
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Compiler detection
+//
+//  V8_CC_GNU     - GCC, or clang in gcc mode
+//  V8_CC_INTEL   - Intel C++
+//  V8_CC_MINGW   - Minimalist GNU for Windows
+//  V8_CC_MINGW32 - Minimalist GNU for Windows (mingw32)
+//  V8_CC_MINGW64 - Minimalist GNU for Windows (mingw-w64)
+//  V8_CC_MSVC    - Microsoft Visual C/C++, or clang in cl.exe mode
+//
+// C++11 feature detection
+//
+//  V8_HAS_CXX11_ALIGNAS        - alignas specifier supported
+//  V8_HAS_CXX11_ALIGNOF        - alignof(type) operator supported
+//  V8_HAS_CXX11_STATIC_ASSERT  - static_assert() supported
+//
+// Compiler-specific feature detection
+//
+//  V8_HAS___ALIGNOF                    - __alignof(type) operator supported
+//  V8_HAS___ALIGNOF__                  - __alignof__(type) operator supported
+//  V8_HAS_ATTRIBUTE_ALIGNED            - __attribute__((aligned(n))) supported
+//  V8_HAS_ATTRIBUTE_ALWAYS_INLINE      - __attribute__((always_inline))
+//                                        supported
+//  V8_HAS_ATTRIBUTE_DEPRECATED         - __attribute__((deprecated)) supported
+//  V8_HAS_ATTRIBUTE_NOINLINE           - __attribute__((noinline)) supported
+//  V8_HAS_ATTRIBUTE_NORETURN           - __attribute__((noreturn)) supported
+//  V8_HAS_ATTRIBUTE_UNUSED             - __attribute__((unused)) supported
+//  V8_HAS_ATTRIBUTE_VISIBILITY         - __attribute__((visibility)) supported
+//  V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT - __attribute__((warn_unused_result))
+//                                        supported
+//  V8_HAS_BUILTIN_CLZ                  - __builtin_clz() supported
+//  V8_HAS_BUILTIN_CTZ                  - __builtin_ctz() supported
+//  V8_HAS_BUILTIN_EXPECT               - __builtin_expect() supported
+//  V8_HAS_BUILTIN_FRAME_ADDRESS        - __builtin_frame_address() supported
+//  V8_HAS_BUILTIN_POPCOUNT             - __builtin_popcount() supported
+//  V8_HAS_BUILTIN_SADD_OVERFLOW        - __builtin_sadd_overflow() supported
+//  V8_HAS_BUILTIN_SSUB_OVERFLOW        - __builtin_ssub_overflow() supported
+//  V8_HAS_BUILTIN_UADD_OVERFLOW        - __builtin_uadd_overflow() supported
+//  V8_HAS_DECLSPEC_ALIGN               - __declspec(align(n)) supported
+//  V8_HAS_DECLSPEC_DEPRECATED          - __declspec(deprecated) supported
+//  V8_HAS_DECLSPEC_NOINLINE            - __declspec(noinline) supported
+//  V8_HAS_DECLSPEC_SELECTANY           - __declspec(selectany) supported
+//  V8_HAS_DECLSPEC_NORETURN            - __declspec(noreturn) supported
+//  V8_HAS___FORCEINLINE                - __forceinline supported
+//
+// Note that testing for compilers and/or features must be done using #if
+// not #ifdef. For example, to test for Intel C++ Compiler, use:
+//  #if V8_CC_INTEL
+//   ...
+//  #endif
+
+#if defined(__clang__)
+
+#if defined(__GNUC__)  // Clang in gcc mode.
+# define V8_CC_GNU 1
+#endif
+
+// Clang defines __alignof__ as alias for __alignof
+# define V8_HAS___ALIGNOF 1
+# define V8_HAS___ALIGNOF__ V8_HAS___ALIGNOF
+
+# define V8_HAS_ATTRIBUTE_ALIGNED (__has_attribute(aligned))
+# define V8_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(always_inline))
+# define V8_HAS_ATTRIBUTE_DEPRECATED (__has_attribute(deprecated))
+# define V8_HAS_ATTRIBUTE_NOINLINE (__has_attribute(noinline))
+# define V8_HAS_ATTRIBUTE_NORETURN (__has_attribute(noreturn))
+# define V8_HAS_ATTRIBUTE_UNUSED (__has_attribute(unused))
+# define V8_HAS_ATTRIBUTE_VISIBILITY (__has_attribute(visibility))
+# define V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT \
+    (__has_attribute(warn_unused_result))
+
+# define V8_HAS_BUILTIN_CLZ (__has_builtin(__builtin_clz))
+# define V8_HAS_BUILTIN_CTZ (__has_builtin(__builtin_ctz))
+# define V8_HAS_BUILTIN_EXPECT (__has_builtin(__builtin_expect))
+# define V8_HAS_BUILTIN_FRAME_ADDRESS (__has_builtin(__builtin_frame_address))
+# define V8_HAS_BUILTIN_POPCOUNT (__has_builtin(__builtin_popcount))
+# define V8_HAS_BUILTIN_SADD_OVERFLOW (__has_builtin(__builtin_sadd_overflow))
+# define V8_HAS_BUILTIN_SSUB_OVERFLOW (__has_builtin(__builtin_ssub_overflow))
+# define V8_HAS_BUILTIN_UADD_OVERFLOW (__has_builtin(__builtin_uadd_overflow))
+
+# define V8_HAS_CXX11_ALIGNAS (__has_feature(cxx_alignas))
+# define V8_HAS_CXX11_STATIC_ASSERT (__has_feature(cxx_static_assert))
+
+#elif defined(__GNUC__)
+
+# define V8_CC_GNU 1
+# if defined(__INTEL_COMPILER)  // Intel C++ also masquerades as GCC 3.2.0
+#  define V8_CC_INTEL 1
+# endif
+# if defined(__MINGW32__)
+#  define V8_CC_MINGW32 1
+# endif
+# if defined(__MINGW64__)
+#  define V8_CC_MINGW64 1
+# endif
+# define V8_CC_MINGW (V8_CC_MINGW32 || V8_CC_MINGW64)
+
+# define V8_HAS___ALIGNOF__ (V8_GNUC_PREREQ(4, 3, 0))
+
+# define V8_HAS_ATTRIBUTE_ALIGNED (V8_GNUC_PREREQ(2, 95, 0))
+// always_inline is available in gcc 4.0 but not very reliable until 4.4.
+// Works around "sorry, unimplemented: inlining failed" build errors with
+// older compilers.
+# define V8_HAS_ATTRIBUTE_ALWAYS_INLINE (V8_GNUC_PREREQ(4, 4, 0))
+# define V8_HAS_ATTRIBUTE_DEPRECATED (V8_GNUC_PREREQ(3, 4, 0))
+# define V8_HAS_ATTRIBUTE_DEPRECATED_MESSAGE (V8_GNUC_PREREQ(4, 5, 0))
+# define V8_HAS_ATTRIBUTE_NOINLINE (V8_GNUC_PREREQ(3, 4, 0))
+# define V8_HAS_ATTRIBUTE_NORETURN (V8_GNUC_PREREQ(2, 5, 0))
+# define V8_HAS_ATTRIBUTE_UNUSED (V8_GNUC_PREREQ(2, 95, 0))
+# define V8_HAS_ATTRIBUTE_VISIBILITY (V8_GNUC_PREREQ(4, 3, 0))
+# define V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT \
+    (!V8_CC_INTEL && V8_GNUC_PREREQ(4, 1, 0))
+
+# define V8_HAS_BUILTIN_CLZ (V8_GNUC_PREREQ(3, 4, 0))
+# define V8_HAS_BUILTIN_CTZ (V8_GNUC_PREREQ(3, 4, 0))
+# define V8_HAS_BUILTIN_EXPECT (V8_GNUC_PREREQ(2, 96, 0))
+# define V8_HAS_BUILTIN_FRAME_ADDRESS (V8_GNUC_PREREQ(2, 96, 0))
+# define V8_HAS_BUILTIN_POPCOUNT (V8_GNUC_PREREQ(3, 4, 0))
+
+// g++ requires -std=c++0x or -std=gnu++0x to support C++11 functionality
+// without warnings (functionality used by the macros below).  These modes
+// are detectable by checking whether __GXX_EXPERIMENTAL_CXX0X__ is defined or,
+// more standardly, by checking whether __cplusplus has a C++11 or greater
+// value. Current versions of g++ do not correctly set __cplusplus, so we check
+// both for forward compatibility.
+# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
+#  define V8_HAS_CXX11_ALIGNAS (V8_GNUC_PREREQ(4, 8, 0))
+#  define V8_HAS_CXX11_ALIGNOF (V8_GNUC_PREREQ(4, 8, 0))
+#  define V8_HAS_CXX11_STATIC_ASSERT (V8_GNUC_PREREQ(4, 3, 0))
+# endif
+#endif
+
+#if defined(_MSC_VER)
+# define V8_CC_MSVC 1
+# define V8_HAS___ALIGNOF 1
+
+# define V8_HAS_DECLSPEC_ALIGN 1
+# define V8_HAS_DECLSPEC_DEPRECATED 1
+# define V8_HAS_DECLSPEC_NOINLINE 1
+# define V8_HAS_DECLSPEC_SELECTANY 1
+# define V8_HAS_DECLSPEC_NORETURN 1
+
+# define V8_HAS___FORCEINLINE 1
+
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Helper macros
+
+// A macro used to make better inlining. Don't bother for debug builds.
+// Use like:
+//   V8_INLINE int GetZero() { return 0; }
+#if !defined(DEBUG) && V8_HAS_ATTRIBUTE_ALWAYS_INLINE
+# define V8_INLINE inline __attribute__((always_inline))
+#elif !defined(DEBUG) && V8_HAS___FORCEINLINE
+# define V8_INLINE __forceinline
+#else
+# define V8_INLINE inline
+#endif
+
+
+// A macro used to tell the compiler to never inline a particular function.
+// Don't bother for debug builds.
+// Use like:
+//   V8_NOINLINE int GetMinusOne() { return -1; }
+#if !defined(DEBUG) && V8_HAS_ATTRIBUTE_NOINLINE
+# define V8_NOINLINE __attribute__((noinline))
+#elif !defined(DEBUG) && V8_HAS_DECLSPEC_NOINLINE
+# define V8_NOINLINE __declspec(noinline)
+#else
+# define V8_NOINLINE /* NOT SUPPORTED */
+#endif
+
+
+// A macro used to tell the compiler that a particular function never returns.
+// Use like:
+//   V8_NORETURN void MyAbort() { abort(); }
+#if V8_HAS_ATTRIBUTE_NORETURN
+# define V8_NORETURN __attribute__((noreturn))
+#elif HAS_DECLSPEC_NORETURN
+# define V8_NORETURN __declspec(noreturn)
+#else
+# define V8_NORETURN /* NOT SUPPORTED */
+#endif
+
+
+// A macro (V8_DEPRECATED) to mark classes or functions as deprecated.
+#if defined(V8_DEPRECATION_WARNINGS) && V8_HAS_ATTRIBUTE_DEPRECATED_MESSAGE
+#define V8_DEPRECATED(message, declarator) \
+  declarator __attribute__((deprecated(message)))
+#elif defined(V8_DEPRECATION_WARNINGS) && V8_HAS_ATTRIBUTE_DEPRECATED
+#define V8_DEPRECATED(message, declarator) \
+  declarator __attribute__((deprecated))
+#elif defined(V8_DEPRECATION_WARNINGS) && V8_HAS_DECLSPEC_DEPRECATED
+#define V8_DEPRECATED(message, declarator) __declspec(deprecated) declarator
+#else
+#define V8_DEPRECATED(message, declarator) declarator
+#endif
+
+
+// A macro (V8_DEPRECATE_SOON) to make it easier to see what will be deprecated.
+#if defined(V8_IMMINENT_DEPRECATION_WARNINGS) && \
+    V8_HAS_ATTRIBUTE_DEPRECATED_MESSAGE
+#define V8_DEPRECATE_SOON(message, declarator) \
+  declarator __attribute__((deprecated(message)))
+#elif defined(V8_IMMINENT_DEPRECATION_WARNINGS) && V8_HAS_ATTRIBUTE_DEPRECATED
+#define V8_DEPRECATE_SOON(message, declarator) \
+  declarator __attribute__((deprecated))
+#elif defined(V8_IMMINENT_DEPRECATION_WARNINGS) && V8_HAS_DECLSPEC_DEPRECATED
+#define V8_DEPRECATE_SOON(message, declarator) __declspec(deprecated) declarator
+#else
+#define V8_DEPRECATE_SOON(message, declarator) declarator
+#endif
+
+
+// A macro to provide the compiler with branch prediction information.
+#if V8_HAS_BUILTIN_EXPECT
+# define V8_UNLIKELY(condition) (__builtin_expect(!!(condition), 0))
+# define V8_LIKELY(condition) (__builtin_expect(!!(condition), 1))
+#else
+# define V8_UNLIKELY(condition) (condition)
+# define V8_LIKELY(condition) (condition)
+#endif
+
+
+// This macro allows to specify memory alignment for structs, classes, etc.
+// Use like:
+//   class V8_ALIGNED(16) MyClass { ... };
+//   V8_ALIGNED(32) int array[42];
+#if V8_HAS_CXX11_ALIGNAS
+# define V8_ALIGNED(n) alignas(n)
+#elif V8_HAS_ATTRIBUTE_ALIGNED
+# define V8_ALIGNED(n) __attribute__((aligned(n)))
+#elif V8_HAS_DECLSPEC_ALIGN
+# define V8_ALIGNED(n) __declspec(align(n))
+#else
+# define V8_ALIGNED(n) /* NOT SUPPORTED */
+#endif
+
+
+// This macro is similar to V8_ALIGNED(), but takes a type instead of size
+// in bytes. If the compiler does not supports using the alignment of the
+// |type|, it will align according to the |alignment| instead. For example,
+// Visual Studio C++ cannot combine __declspec(align) and __alignof. The
+// |alignment| must be a literal that is used as a kind of worst-case fallback
+// alignment.
+// Use like:
+//   struct V8_ALIGNAS(AnotherClass, 16) NewClass { ... };
+//   V8_ALIGNAS(double, 8) int array[100];
+#if V8_HAS_CXX11_ALIGNAS
+# define V8_ALIGNAS(type, alignment) alignas(type)
+#elif V8_HAS___ALIGNOF__ && V8_HAS_ATTRIBUTE_ALIGNED
+# define V8_ALIGNAS(type, alignment) __attribute__((aligned(__alignof__(type))))
+#else
+# define V8_ALIGNAS(type, alignment) V8_ALIGNED(alignment)
+#endif
+
+
+// This macro returns alignment in bytes (an integer power of two) required for
+// any instance of the given type, which is either complete type, an array type,
+// or a reference type.
+// Use like:
+//   size_t alignment = V8_ALIGNOF(double);
+#if V8_HAS_CXX11_ALIGNOF
+# define V8_ALIGNOF(type) alignof(type)
+#elif V8_HAS___ALIGNOF
+# define V8_ALIGNOF(type) __alignof(type)
+#elif V8_HAS___ALIGNOF__
+# define V8_ALIGNOF(type) __alignof__(type)
+#else
+// Note that alignment of a type within a struct can be less than the
+// alignment of the type stand-alone (because of ancient ABIs), so this
+// should only be used as a last resort.
+namespace v8 { template <typename T> class AlignOfHelper { char c; T t; }; }
+# define V8_ALIGNOF(type) (sizeof(::v8::AlignOfHelper<type>) - sizeof(type))
+#endif
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+//   int foo() WARN_UNUSED_RESULT;
+#if V8_HAS_ATTRIBUTE_WARN_UNUSED_RESULT
+#define V8_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define V8_WARN_UNUSED_RESULT /* NOT SUPPORTED */
+#endif
+
+// clang-format on
+
+#endif  // V8CONFIG_H_
diff --git a/Plugins/UnrealJS/UnrealJS.uplugin b/Plugins/UnrealJS/UnrealJS.uplugin
new file mode 100644
index 0000000..ebc1677
--- /dev/null
+++ b/Plugins/UnrealJS/UnrealJS.uplugin
@@ -0,0 +1,40 @@
+{
+  "FileVersion": 3,
+  "FriendlyName": "Unreal.js - Javascript Runtime",
+  "Version": 1,
+  "VersionName": "0.1",
+  "FriendlyVersion": "0.1",
+  "EngineVersion": "4.10",
+  "Description": "Javascript powered UnrealEngine",
+  "Category": "Programming",
+  "CreatedBy": "NCSOFT Corporation",
+  "CreatedByURL": "http://github.com/ncsoft",
+  "EnabledByDefault": true,
+  "Modules": [
+    {
+      "Name": "V8",
+      "Type": "Runtime",
+      "LoadingPhase": "PreDefault",
+      "WhitelistPlatforms": [ "Win64", "Win32" ]
+    },
+    {
+      "Name": "JavascriptUMG",
+      "Type": "Runtime",
+      "LoadingPhase": "PreDefault"
+    },
+    {
+      "Name": "JavascriptHttp",
+      "Type": "Runtime",
+      "LoadingPhase": "PreDefault"
+    },
+    {
+      "Name": "JavascriptEditor",
+      "Type": "Editor"
+    },
+    {
+      "Name": "JavascriptConsole",
+      "Type": "Editor"
+    }
+  ],
+  "CanContainContent": true
+}
\ No newline at end of file
