From fbe1de77ec1ce5977f8309ecedf29009dc4d283c Mon Sep 17 00:00:00 2001 From: Chris Marisic Date: Wed, 27 Aug 2014 11:32:37 -0400 Subject: [PATCH 001/859] Fix 404 Fix typo in MsBuild hyperlink that results in a 404 --- site/jekyll/guides/es6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/jekyll/guides/es6.md b/site/jekyll/guides/es6.md index f3fc4d366..fa3911823 100644 --- a/site/jekyll/guides/es6.md +++ b/site/jekyll/guides/es6.md @@ -63,7 +63,7 @@ ReactSiteConfiguration.Configuration .SetUseHarmony(true) .AddScript("~/Content/Sample.jsx"); ``` -If you are using [MSBuild to precompile your JSX](/guide/msbuild.html), you also need to enable it in MSBuild via the `UseHarmony="true"` flag in your build script (`TransformJsx.proj` by default): +If you are using [MSBuild to precompile your JSX](/guides/msbuild.html), you also need to enable it in MSBuild via the `UseHarmony="true"` flag in your build script (`TransformJsx.proj` by default): ```xml From 60cba9096151df31c21bf06032597a7a5b560cce Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 14 Sep 2014 18:07:05 -0700 Subject: [PATCH 002/859] Allow namespaced components to be rendered server-side. Closes #37 --- src/React.Tests/Core/ReactComponentTest.cs | 26 +++++++++++++++-- src/React/ReactComponent.cs | 33 +++++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/React.Tests/Core/ReactComponentTest.cs b/src/React.Tests/Core/ReactComponentTest.cs index 72d48130f..afe6a47de 100644 --- a/src/React.Tests/Core/ReactComponentTest.cs +++ b/src/React.Tests/Core/ReactComponentTest.cs @@ -20,7 +20,7 @@ public class ReactComponentTest public void RenderHtmlShouldThrowExceptionIfComponentDoesNotExist() { var environment = new Mock(); - environment.Setup(x => x.HasVariable("Foo")).Returns(false); + environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(false); var component = new ReactComponent(environment.Object, "Foo", "container"); Assert.Throws(() => @@ -33,7 +33,7 @@ public void RenderHtmlShouldThrowExceptionIfComponentDoesNotExist() public void RenderHtmlShouldCallRenderComponent() { var environment = new Mock(); - environment.Setup(x => x.HasVariable("Foo")).Returns(true); + environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); var component = new ReactComponent(environment.Object, "Foo", "container") { @@ -48,7 +48,7 @@ public void RenderHtmlShouldCallRenderComponent() public void RenderHtmlShouldWrapComponentInDiv() { var environment = new Mock(); - environment.Setup(x => x.HasVariable("Foo")).Returns(true); + environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); environment.Setup(x => x.Execute(@"React.renderComponentToString(Foo({""hello"":""World""}))")) .Returns("[HTML]"); @@ -77,5 +77,25 @@ public void RenderJavaScriptShouldCallRenderComponent() result ); } + + [TestCase("Foo", true)] + [TestCase("Foo.Bar", true)] + [TestCase("Foo.Bar.Baz", true)] + [TestCase("alert()", false)] + [TestCase("Foo.alert()", false)] + [TestCase("lol what", false)] + public void TestEnsureComponentNameValid(string input, bool expected) + { + var isValid = true; + try + { + ReactComponent.EnsureComponentNameValid(input); + } + catch (ReactInvalidComponentException) + { + isValid = false; + } + Assert.AreEqual(expected, isValid); + } } } diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index cbafaf959..24371467a 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +using System.Linq; +using System.Text.RegularExpressions; using Newtonsoft.Json; using React.Exceptions; @@ -17,6 +19,13 @@ namespace React /// public class ReactComponent : IReactComponent { + /// + /// Regular expression used to validate JavaScript identifiers. Used to ensure component + /// names are valid. + /// Based off https://gist.github.com/Daniel15/3074365 + /// + private static readonly Regex _identifierRegex = new Regex(@"^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:"".+""|\'.+\'|\d+)\])*?$", RegexOptions.Compiled); + /// /// Environment this component has been created in /// @@ -45,6 +54,7 @@ public class ReactComponent : IReactComponent /// The ID of the container DIV for this component public ReactComponent(IReactEnvironment environment, string componentName, string containerId) { + EnsureComponentNameValid(componentName); _environment = environment; _componentName = componentName; _containerId = containerId; @@ -89,7 +99,12 @@ public string RenderJavaScript() /// private void EnsureComponentExists() { - if (!_environment.HasVariable(_componentName)) + // This is safe as componentName was validated via EnsureComponentNameValid() + var componentExists = _environment.Execute(string.Format( + "typeof {0} !== 'undefined'", + _componentName + )); + if (!componentExists) { throw new ReactInvalidComponentException(string.Format( "Could not find a component named '{0}'. Did you forget to add it to " + @@ -112,5 +127,21 @@ private string GetComponentInitialiser() encodedProps ); } + + /// + /// Validates that the specified component name is valid + /// + /// + internal static void EnsureComponentNameValid(string componentName) + { + var isValid = componentName.Split('.').All(segment => _identifierRegex.IsMatch(segment)); + if (!isValid) + { + throw new ReactInvalidComponentException(string.Format( + "Invalid component name '{0}'", + componentName + )); + } + } } } From f13c0e5dfdef25980fadeaf8c0e0fc7fcf36d985 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 14 Sep 2014 18:45:24 -0700 Subject: [PATCH 003/859] Include more detail in error messages. Closes #17 --- .../Exceptions/ReactScriptLoadException.cs | 41 +++++++++ .../ReactServerRenderingException.cs | 41 +++++++++ src/React/IReactEnvironment.cs | 9 ++ src/React/JsxTransformer.cs | 14 ++- src/React/React.csproj | 2 + src/React/ReactComponent.cs | 31 +++++-- src/React/ReactEnvironment.cs | 88 +++++++++++++++++-- src/React/Resources/shims.js | 6 +- 8 files changed, 215 insertions(+), 17 deletions(-) create mode 100644 src/React/Exceptions/ReactScriptLoadException.cs create mode 100644 src/React/Exceptions/ReactServerRenderingException.cs diff --git a/src/React/Exceptions/ReactScriptLoadException.cs b/src/React/Exceptions/ReactScriptLoadException.cs new file mode 100644 index 000000000..733939c63 --- /dev/null +++ b/src/React/Exceptions/ReactScriptLoadException.cs @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; +using System.Runtime.Serialization; + +namespace React.Exceptions +{ + /// + /// Thrown when an error is encountered while loading a JavaScript file. + /// + [Serializable] + public class ReactScriptLoadException : ReactException + { + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public ReactScriptLoadException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public ReactScriptLoadException(string message, Exception innerException) + : base(message, innerException) { } + + /// + /// Used by deserialization + /// + protected ReactScriptLoadException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} diff --git a/src/React/Exceptions/ReactServerRenderingException.cs b/src/React/Exceptions/ReactServerRenderingException.cs new file mode 100644 index 000000000..d07ab4a29 --- /dev/null +++ b/src/React/Exceptions/ReactServerRenderingException.cs @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; +using System.Runtime.Serialization; + +namespace React.Exceptions +{ + /// + /// Thrown when an error occurs during server rendering of a React component. + /// + [Serializable] + public class ReactServerRenderingException : ReactException + { + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public ReactServerRenderingException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public ReactServerRenderingException(string message, Exception innerException) + : base(message, innerException) { } + + /// + /// Used by deserialization + /// + protected ReactServerRenderingException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} diff --git a/src/React/IReactEnvironment.cs b/src/React/IReactEnvironment.cs index 9fa518cd3..b14f8b2ff 100644 --- a/src/React/IReactEnvironment.cs +++ b/src/React/IReactEnvironment.cs @@ -41,6 +41,15 @@ public interface IReactEnvironment /// Result of the JavaScript code T Execute(string code); + /// + /// Executes the provided JavaScript function, returning a result of the specified type. + /// + /// Type to return + /// JavaScript function to execute + /// Arguments to pass to function + /// Result of the JavaScript code + T Execute(string function, params object[] args); + /// /// Attempts to execute the provided JavaScript code using the current engine. If an /// exception is thrown, retries the execution using a new thread (and hence a new engine) diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index 696f91f58..bd0e4bc52 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -98,7 +98,19 @@ public string TransformJsxFile(string filename, bool? useHarmony = null) } // 3. Not cached, perform the transformation - return TransformJsxWithHeader(contents, hash, useHarmony); + try + { + return TransformJsxWithHeader(contents, hash, useHarmony); + } + catch (JsxException ex) + { + // Add the filename to the error message + throw new JsxException(string.Format( + "In file \"{0}\": {1}", + filename, + ex.Message + )); + } } ); } diff --git a/src/React/React.csproj b/src/React/React.csproj index cae539eac..8aadeb367 100644 --- a/src/React/React.csproj +++ b/src/React/React.csproj @@ -83,6 +83,8 @@ Properties\SharedAssemblyVersionInfo.cs + + diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index 24371467a..4defe1302 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text.RegularExpressions; +using JavaScriptEngineSwitcher.Core; using Newtonsoft.Json; using React.Exceptions; @@ -68,15 +69,27 @@ public ReactComponent(IReactEnvironment environment, string componentName, strin public string RenderHtml() { EnsureComponentExists(); - var html = _environment.Execute( - string.Format("React.renderComponentToString({0})", GetComponentInitialiser()) - ); - // TODO: Allow changing of the wrapper tag element from a DIV to something else - return string.Format( - "
{1}
", - _containerId, - html - ); + try + { + var html = _environment.Execute( + string.Format("React.renderComponentToString({0})", GetComponentInitialiser()) + ); + // TODO: Allow changing of the wrapper tag element from a DIV to something else + return string.Format( + "
{1}
", + _containerId, + html + ); + } + catch (JsRuntimeException ex) + { + throw new ReactServerRenderingException(string.Format( + "Error while rendering \"{0}\" to \"{2}\": {1}", + _componentName, + ex.Message, + _containerId + )); + } } /// diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index 14f46b27d..9a30b86a0 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -14,6 +14,7 @@ using System.Text; using System.Threading; using JavaScriptEngineSwitcher.Core; +using React.Exceptions; namespace React { @@ -169,7 +170,18 @@ private void EnsureUserScriptsLoaded() foreach (var file in _config.Scripts) { var contents = JsxTransformer.TransformJsxFile(file); - Execute(contents); + try + { + Execute(contents); + } + catch (JsRuntimeException ex) + { + throw new ReactScriptLoadException(string.Format( + "Error while loading \"{0}\": {1}", + file, + ex.Message + )); + } } Engine.SetVariableValue(USER_SCRIPTS_LOADED_KEY, true); } @@ -180,7 +192,14 @@ private void EnsureUserScriptsLoaded() /// JavaScript to execute public void Execute(string code) { - Engine.Execute(code); + try + { + Engine.Execute(code); + } + catch (JsRuntimeException ex) + { + throw WrapJavaScriptRuntimeException(ex); + } } /// @@ -191,7 +210,33 @@ public void Execute(string code) /// Result of the JavaScript code public T Execute(string code) { - return Engine.Evaluate(code); + try + { + return Engine.Evaluate(code); + } + catch (JsRuntimeException ex) + { + throw WrapJavaScriptRuntimeException(ex); + } + } + + /// + /// Executes the provided JavaScript function, returning a result of the specified type. + /// + /// Type to return + /// JavaScript function to execute + /// Arguments to pass to function + /// Result of the JavaScript code + public T Execute(string function, params object[] args) + { + try + { + return Engine.CallFunction(function, args); + } + catch (JsRuntimeException ex) + { + throw WrapJavaScriptRuntimeException(ex); + } } /// @@ -201,7 +246,14 @@ public T Execute(string code) /// true if the variable exists; false otherwise public bool HasVariable(string name) { - return Engine.HasVariable(name); + try + { + return Engine.HasVariable(name); + } + catch (JsRuntimeException ex) + { + throw WrapJavaScriptRuntimeException(ex); + } } /// @@ -278,7 +330,7 @@ public T ExecuteWithLargerStackIfRequired(string function, params object[] ar { try { - return Engine.CallFunction(function, args); + return Execute(function, args); } catch (Exception) { @@ -292,7 +344,7 @@ public T ExecuteWithLargerStackIfRequired(string function, params object[] ar try { // New engine will be created here (as this is a new thread) - result = Engine.CallFunction(function, args); + result = Execute(function, args); } catch (Exception threadEx) { @@ -336,5 +388,29 @@ public void Dispose() { _engineFactory.DisposeEngineForCurrentThread(); } + + /// + /// Updates the Message of a to be more useful, containing + /// the line and column numbers. + /// + /// Original exception + /// New exception + private JsRuntimeException WrapJavaScriptRuntimeException(JsRuntimeException ex) + { + return new JsRuntimeException(string.Format( + "{0}\r\nLine: {1}\r\nColumn:{2}", + ex.Message, + ex.LineNumber, + ex.ColumnNumber + ), ex.EngineName, ex.EngineVersion) + { + ErrorCode = ex.ErrorCode, + Category = ex.Category, + LineNumber = ex.LineNumber, + ColumnNumber = ex.ColumnNumber, + SourceFragment = ex.SourceFragment, + Source = ex.Source, + }; + } } } diff --git a/src/React/Resources/shims.js b/src/React/Resources/shims.js index 96c60ca56..568a7691e 100644 --- a/src/React/Resources/shims.js +++ b/src/React/Resources/shims.js @@ -20,5 +20,9 @@ if (!Object.freeze) { } function ReactNET_transform(input, harmony) { - return global.JSXTransformer.transform(input, { harmony: !!harmony }).code; + try { + return global.JSXTransformer.transform(input, { harmony: !!harmony }).code; + } catch (ex) { + throw new Error(ex.message + " (at line " + ex.lineNumber + " column " + ex.column + ")"); + } } \ No newline at end of file From 4f20c5d49032992897a1309888e887ef95a08a29 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 14 Sep 2014 18:46:20 -0700 Subject: [PATCH 004/859] Bump version number to 1.1.2 --- build.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.proj b/build.proj index a175ac947..50e822baa 100644 --- a/build.proj +++ b/build.proj @@ -11,7 +11,7 @@ of patent rights can be found in the PATENTS file in the same directory. 1 1 - 1 + 2 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks From 9da55540fe9a40f2e6d6f9e54cced41d4b90df57 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 14 Sep 2014 19:00:12 -0700 Subject: [PATCH 005/859] Blog post for 1.1.2 release --- site/jekyll/_posts/2014-09-14-1.1.2-release.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 site/jekyll/_posts/2014-09-14-1.1.2-release.md diff --git a/site/jekyll/_posts/2014-09-14-1.1.2-release.md b/site/jekyll/_posts/2014-09-14-1.1.2-release.md new file mode 100644 index 000000000..f19dde050 --- /dev/null +++ b/site/jekyll/_posts/2014-09-14-1.1.2-release.md @@ -0,0 +1,15 @@ +--- +title: "ReactJS.NET 1.1.2 - Better errors messages and namespaced JSX" +layout: post +author: Daniel Lo Nigro +--- + +I'm happy to announce the release of ReactJS.NET 1.1.2! This is a minor release and includes a number of changes and fixes since version 1.1.1: + + * Error messages now contain more details, including file name, component name, line number and column number. This should make debugging a bit easier, especially if you're debugging without Visual Studio attached. ([#17](https://github.com/reactjs/React.NET/issues/17)) + * [JSX Namespacing](http://facebook.github.io/react/blog/2014/07/17/react-v0.11.html#jsx-namespacing) is now supported ([#37](https://github.com/reactjs/React.NET/issues/37)) + +Have fun, and as always, please feel free to send feedback or bug reports +[on GitHub](https://github.com/reactjs/React.NET). + +— Daniel From 6525710a405d1328220a9e07652e6031a8e6e180 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 14 Sep 2014 19:01:04 -0700 Subject: [PATCH 006/859] Bump version number for dev --- build.proj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.proj b/build.proj index 50e822baa..b538364f7 100644 --- a/build.proj +++ b/build.proj @@ -11,7 +11,7 @@ of patent rights can be found in the PATENTS file in the same directory. 1 1 - 2 + 3 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks @@ -30,7 +30,7 @@ of patent rights can be found in the PATENTS file in the same directory. - @@ -88,7 +88,7 @@ of patent rights can be found in the PATENTS file in the same directory. - + - \ No newline at end of file + From ec1316409083c55ea98f79d97a78300719048be6 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 14 Sep 2014 19:05:55 -0700 Subject: [PATCH 007/859] Include date in post on home page --- site/jekyll/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/site/jekyll/index.md b/site/jekyll/index.md index 7cf1ff96b..d937e839a 100644 --- a/site/jekyll/index.md +++ b/site/jekyll/index.md @@ -27,6 +27,7 @@ id: home Latest news: {{ site.posts.first.title }} + ({{ site.posts.first.date | date: "%B %e, %Y" }})

From 74be3368ac57fecb5868ff6dcbb3fc58ab23ce4f Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Fri, 31 Oct 2014 22:22:56 -0700 Subject: [PATCH 008/859] Upgrade React to 0.12.0 --- site/jekyll/getting-started/tutorial.md | 8 +- site/jekyll/guides/cassette.md | 2 +- site/jekyll/guides/server-side-rendering.md | 4 +- site/jekyll/index.md | 2 +- .../Views/Home/Index.cshtml | 2 +- src/React.Sample.Mvc4/Views/Home/Index.cshtml | 2 +- src/React/Resources/JSXTransformer.js | 2336 +++--- src/React/Resources/react-with-addons.js | 6600 ++++++++--------- 8 files changed, 4459 insertions(+), 4497 deletions(-) diff --git a/site/jekyll/getting-started/tutorial.md b/site/jekyll/getting-started/tutorial.md index 749a277c3..1a4333929 100644 --- a/site/jekyll/getting-started/tutorial.md +++ b/site/jekyll/getting-started/tutorial.md @@ -66,7 +66,7 @@ Replace the contents of the new view file with the following:
- + @@ -239,7 +239,7 @@ Markdown is a simple way to format your text inline. For example, surrounding te First, add the third-party **Showdown** library to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. We will add it via NuGet (search for "Showdown" and install it, similar to how you installed ReactJS.NET earlier) and reference the script tag in your view: ```html{2} - + ``` @@ -860,7 +860,7 @@ Now that the bundle has been registered, we need to reference it from the view:
- + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() @@ -948,7 +948,7 @@ In the view, we will accept the list of comments as the model, and use `Html.Rea submitUrl = Url.Action("AddComment"), pollInterval = 2000, }) - + @Html.ReactInitJavaScript() diff --git a/site/jekyll/guides/cassette.md b/site/jekyll/guides/cassette.md index 648a067e4..48e7cf12c 100644 --- a/site/jekyll/guides/cassette.md +++ b/site/jekyll/guides/cassette.md @@ -36,7 +36,7 @@ render from your view using Cassette: ... - + @Bundles.RenderScripts() ``` diff --git a/site/jekyll/guides/server-side-rendering.md b/site/jekyll/guides/server-side-rendering.md index e50ce6b0c..753da189e 100644 --- a/site/jekyll/guides/server-side-rendering.md +++ b/site/jekyll/guides/server-side-rendering.md @@ -47,7 +47,7 @@ code. ```html - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() ``` @@ -62,7 +62,7 @@ code.
- + ``` diff --git a/site/jekyll/index.md b/site/jekyll/index.md index d937e839a..9f4bab50e 100644 --- a/site/jekyll/index.md +++ b/site/jekyll/index.md @@ -106,7 +106,7 @@ bundles.Add(new JsxBundle("~/bundles/main").Include( }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() ``` diff --git a/src/React.Sample.Cassette/Views/Home/Index.cshtml b/src/React.Sample.Cassette/Views/Home/Index.cshtml index be13554f3..6eb40ebbf 100644 --- a/src/React.Sample.Cassette/Views/Home/Index.cshtml +++ b/src/React.Sample.Cassette/Views/Home/Index.cshtml @@ -20,7 +20,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Bundles.RenderScripts() @Html.ReactInitJavaScript() diff --git a/src/React.Sample.Mvc4/Views/Home/Index.cshtml b/src/React.Sample.Mvc4/Views/Home/Index.cshtml index d6503011a..3fd670119 100644 --- a/src/React.Sample.Mvc4/Views/Home/Index.cshtml +++ b/src/React.Sample.Mvc4/Views/Home/Index.cshtml @@ -17,7 +17,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() diff --git a/src/React/Resources/JSXTransformer.js b/src/React/Resources/JSXTransformer.js index 7aad3782a..34f87a2fb 100644 --- a/src/React/Resources/JSXTransformer.js +++ b/src/React/Resources/JSXTransformer.js @@ -1,7 +1,336 @@ /** - * JSXTransformer v0.11.1 + * JSXTransformer v0.12.0 */ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.JSXTransformer=e()}}(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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { + source += ' (' + inlineScriptCount + ')'; + } + } else if (dummyAnchor) { + // Firefox has problems when the sourcemap source is a proper URL with a + // protocol and hostname, so use the pathname. We could use just the + // filename, but hopefully using the full path will prevent potential + // issues where the same filename exists in multiple directories. + dummyAnchor.href = url; + source = dummyAnchor.pathname.substr(1); + } + map.sources = [source]; + map.sourcesContent = [code]; + + return ( + transformed.code + + '\n//# sourceMappingURL=data:application/json;base64,' + + buffer.Buffer(JSON.stringify(map)).toString('base64') + ); +} + + +/** + * Appends a script element at the end of the with the content of code, + * after transforming it. + * + * @param {string} code The original source code + * @param {string?} url Where the code came from. null if inline + * @param {object?} options Options to pass to jstransform + * @internal + */ +function run(code, url, options) { + var scriptEl = document.createElement('script'); + scriptEl.text = transformCode(code, url, options); + headEl.appendChild(scriptEl); +} + +/** + * Load script from the provided url and pass the content to the callback. + * + * @param {string} url The location of the script src + * @param {function} callback Function to call with the content of url + * @internal + */ +function load(url, successCallback, errorCallback) { + var xhr; + xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') + : new XMLHttpRequest(); + + // async, however scripts will be executed in the order they are in the + // DOM to mirror normal script loading. + xhr.open('GET', url, true); + if ('overrideMimeType' in xhr) { + xhr.overrideMimeType('text/plain'); + } + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + if (xhr.status === 0 || xhr.status === 200) { + successCallback(xhr.responseText); + } else { + errorCallback(); + throw new Error("Could not load " + url); + } + } + }; + return xhr.send(null); +} + +/** + * Loop over provided script tags and get the content, via innerHTML if an + * inline script, or by using XHR. Transforms are applied if needed. The scripts + * are executed in the order they are found on the page. + * + * @param {array} scripts The - + ``` The server-rendered HTML will automatically be reused by React client-side, From 88589d72ad6f11d08d7d8f2c428e69ac143caffa Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Fri, 31 Oct 2014 23:00:20 -0700 Subject: [PATCH 010/859] Remove `@jsx` pragma requirement. Closes #40 --- README.md | 1 - src/React.Sample.Cassette/Content/Sample.jsx | 2 -- src/React.Sample.Mvc4/Content/Sample.jsx | 2 -- src/React.Tests/Core/JsxTransformerTests.cs | 31 +++++++------------ src/React/JsxTransformer.cs | 6 ---- .../JsxBundle.cs | 16 ---------- 6 files changed, 11 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index e60dd314a..d67eb2f81 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ Install-Package React.Web.Mvc4 Create JSX files ```javascript // /Scripts/HelloWorld.jsx -/** @jsx React.DOM */ var HelloWorld = React.createClass({ render: function () { return ( diff --git a/src/React.Sample.Cassette/Content/Sample.jsx b/src/React.Sample.Cassette/Content/Sample.jsx index f09cd14af..05a30a6b4 100644 --- a/src/React.Sample.Cassette/Content/Sample.jsx +++ b/src/React.Sample.Cassette/Content/Sample.jsx @@ -5,8 +5,6 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @jsx React.DOM */ var CommentsBox = React.createClass({ diff --git a/src/React.Sample.Mvc4/Content/Sample.jsx b/src/React.Sample.Mvc4/Content/Sample.jsx index 1134b4f41..fa3e1c4ff 100644 --- a/src/React.Sample.Mvc4/Content/Sample.jsx +++ b/src/React.Sample.Mvc4/Content/Sample.jsx @@ -5,8 +5,6 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @jsx React.DOM */ var CommentsBox = React.createClass({ diff --git a/src/React.Tests/Core/JsxTransformerTests.cs b/src/React.Tests/Core/JsxTransformerTests.cs index ba25d9142..b7c46bc0a 100644 --- a/src/React.Tests/Core/JsxTransformerTests.cs +++ b/src/React.Tests/Core/JsxTransformerTests.cs @@ -46,23 +46,14 @@ public void SetUp() } [Test] - public void ShouldNotTransformJsxIfNoAnnotationPresent() + public void ShouldTransformJsx() { const string input = "
Hello World
"; - - var output = _jsxTransformer.TransformJsx(input); - Assert.AreEqual(input, output); - } - - [Test] - public void ShouldTransformJsxIfAnnotationPresent() - { - const string input = "/** @jsx React.DOM */
Hello World
"; _jsxTransformer.TransformJsx(input); _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", - "/** @jsx React.DOM */
Hello World
", + "
Hello World
", false )); } @@ -72,11 +63,11 @@ public void ShouldWrapExceptionsInJsxExeption() { _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", - "/** @jsx React.DOM */
Hello World
", + "
Hello World
", false )).Throws(new Exception("Something broke...")); - const string input = "/** @jsx React.DOM */
Hello World
"; + const string input = "
Hello World
"; Assert.Throws(() => _jsxTransformer.TransformJsx(input)); } @@ -87,7 +78,7 @@ public void ShouldThrowIfEngineNotSupported() Assert.Throws(() => { - _jsxTransformer.TransformJsx("/** @jsx React.DOM */
Hello world
"); + _jsxTransformer.TransformJsx("
Hello world
"); }); } @@ -124,13 +115,13 @@ public void ShouldTransformJsxIfFileCacheHashInvalid() SetUpEmptyCache(); _fileSystem.Setup(x => x.FileExists("foo.generated.js")).Returns(true); _fileSystem.Setup(x => x.ReadAsString("foo.generated.js")).Returns("/* filesystem cached invalid */"); - _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("/** @jsx React.DOM */
Hello World
"); + _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); _fileCacheHash.Setup(x => x.ValidateHash(It.IsAny(), It.IsAny())).Returns(false); _jsxTransformer.TransformJsxFile("foo.jsx"); _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", - "/** @jsx React.DOM */
Hello World
", + "
Hello World
", false )); } @@ -140,12 +131,12 @@ public void ShouldTransformJsxIfNoCache() { SetUpEmptyCache(); _fileSystem.Setup(x => x.FileExists("foo.generated.js")).Returns(false); - _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("/** @jsx React.DOM */
Hello World
"); + _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); _jsxTransformer.TransformJsxFile("foo.jsx"); _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", - "/** @jsx React.DOM */
Hello World
", + "
Hello World
", false )); } @@ -153,10 +144,10 @@ public void ShouldTransformJsxIfNoCache() [Test] public void ShouldSaveTransformationResult() { - _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("/** @jsx React.DOM */
Hello World
"); + _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", - "/** @jsx React.DOM */
Hello World
", + "
Hello World
", false )).Returns("React.DOM.div('Hello World')"); diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index bd0e4bc52..2a5ccd4bb 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -141,12 +141,6 @@ private string TransformJsxWithHeader(string contents, string hash = null, bool? /// JavaScript public string TransformJsx(string input, bool? useHarmony = null) { - // Just return directly if there's no JSX annotation - if (!input.Contains("@jsx")) - { - return input; - } - EnsureJsxTransformerSupported(); try { diff --git a/src/System.Web.Optimization.React/JsxBundle.cs b/src/System.Web.Optimization.React/JsxBundle.cs index eb1353477..0e1a32c35 100644 --- a/src/System.Web.Optimization.React/JsxBundle.cs +++ b/src/System.Web.Optimization.React/JsxBundle.cs @@ -26,22 +26,6 @@ public JsxBundle(string virtualPath) : base(virtualPath, GetTransforms()) base.ConcatenationToken = ";" + Environment.NewLine; } - /// - /// Applies the transformations. - /// - /// The bundle response. - public override BundleResponse ApplyTransforms(BundleContext context, string bundleContent, Collections.Generic.IEnumerable bundleFiles) - { - const string pragma = "/** @jsx React.DOM */"; - - if (!bundleContent.TrimStart().StartsWith(pragma)) - { - bundleContent = pragma + bundleContent; - } - - return base.ApplyTransforms(context, bundleContent, bundleFiles); - } - /// /// Gets the transformations that should be used by the bundle. /// From 488a60e7bc1c07f740ffbff18ec68143da8fbed3 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Fri, 31 Oct 2014 23:09:13 -0700 Subject: [PATCH 011/859] Change React.renderComponent to React.render --- src/React.Tests/Core/ReactComponentTest.cs | 6 +++--- src/React/ReactComponent.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/React.Tests/Core/ReactComponentTest.cs b/src/React.Tests/Core/ReactComponentTest.cs index afe6a47de..73f7b2e1b 100644 --- a/src/React.Tests/Core/ReactComponentTest.cs +++ b/src/React.Tests/Core/ReactComponentTest.cs @@ -41,7 +41,7 @@ public void RenderHtmlShouldCallRenderComponent() }; component.RenderHtml(); - environment.Verify(x => x.Execute(@"React.renderComponentToString(Foo({""hello"":""World""}))")); + environment.Verify(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")); } [Test] @@ -49,7 +49,7 @@ public void RenderHtmlShouldWrapComponentInDiv() { var environment = new Mock(); environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); - environment.Setup(x => x.Execute(@"React.renderComponentToString(Foo({""hello"":""World""}))")) + environment.Setup(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")) .Returns("[HTML]"); var component = new ReactComponent(environment.Object, "Foo", "container") @@ -73,7 +73,7 @@ public void RenderJavaScriptShouldCallRenderComponent() var result = component.RenderJavaScript(); Assert.AreEqual( - @"React.renderComponent(Foo({""hello"":""World""}), document.getElementById(""container""))", + @"React.render(Foo({""hello"":""World""}), document.getElementById(""container""))", result ); } diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index 4defe1302..7332c765e 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -72,7 +72,7 @@ public string RenderHtml() try { var html = _environment.Execute( - string.Format("React.renderComponentToString({0})", GetComponentInitialiser()) + string.Format("React.renderToString({0})", GetComponentInitialiser()) ); // TODO: Allow changing of the wrapper tag element from a DIV to something else return string.Format( @@ -101,7 +101,7 @@ public string RenderHtml() public string RenderJavaScript() { return string.Format( - "React.renderComponent({0}, document.getElementById({1}))", + "React.render({0}, document.getElementById({1}))", GetComponentInitialiser(), JsonConvert.SerializeObject(_containerId) ); From da8e202bcd4aad66c06d2559e1272e4a63303bfc Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 2 Nov 2014 18:51:41 -0800 Subject: [PATCH 012/859] Add support for V8 on Windows via ClearScript --- .gitignore | 1 + build.proj | 1 + .../AssemblyRegistration.cs | 40 +++++++++ .../ClearScriptV8InitialisationException.cs | 46 +++++++++++ .../ClearScriptV8Utils.cs | 55 +++++++++++++ .../Properties/AssemblyInfo.cs | 16 ++++ ...eact.JavaScriptEngine.ClearScriptV8.csproj | 82 +++++++++++++++++++ ...act.JavaScriptEngine.ClearScriptV8.nutrans | 21 +++++ .../packages.config | 5 ++ .../React.JavaScriptEngine.VroomJs.csproj | 5 +- .../packages.config | 2 +- .../React.Sample.Mvc4.csproj | 28 +++++++ src/React.Sample.Mvc4/Web.config | 4 + src/React.Sample.Mvc4/packages.config | 2 + src/React.Tests/React.Tests.csproj | 4 +- src/React.Tests/packages.config | 2 +- src/React.sln | 7 ++ src/React/AssemblyRegistration.cs | 4 +- src/React/React.csproj | 28 +++---- src/React/packages.config | 8 +- 20 files changed, 335 insertions(+), 26 deletions(-) create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/AssemblyRegistration.cs create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8InitialisationException.cs create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8Utils.cs create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/Properties/AssemblyInfo.cs create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.csproj create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.nutrans create mode 100644 src/React.JavaScriptEngine.ClearScriptV8/packages.config diff --git a/.gitignore b/.gitignore index ed7ef684c..f75550ee1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ src/**/*.nuspec !src/template.nuspec site/jekyll/_site src/React.Sample.Cassette/cassette-cache +src/React.Sample.Mvc4/ClearScript.V8 *.generated.js ## Ignore Visual Studio temporary files, build results, and diff --git a/build.proj b/build.proj index b538364f7..cedb1acf0 100644 --- a/build.proj +++ b/build.proj @@ -27,6 +27,7 @@ of patent rights can be found in the PATENTS file in the same directory. + diff --git a/src/React.JavaScriptEngine.ClearScriptV8/AssemblyRegistration.cs b/src/React.JavaScriptEngine.ClearScriptV8/AssemblyRegistration.cs new file mode 100644 index 000000000..0e6d81d7c --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/AssemblyRegistration.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using JavaScriptEngineSwitcher.V8; +using React.TinyIoC; + +namespace React.JavaScriptEngine.ClearScriptV8 +{ + /// + /// Handles registration of ClearScript V8 for ReactJS.NET. + /// + public class AssemblyRegistration : IAssemblyRegistration + { + /// + /// Registers components in the React IoC container + /// + /// Container to register components in + public void Register(TinyIoCContainer container) + { + // Only supported on Windows + if (!ClearScriptV8Utils.IsEnvironmentSupported()) + { + return; + } + + ClearScriptV8Utils.EnsureEngineFunctional(); + container.Register(new JavaScriptEngineFactory.Registration + { + Factory = () => new V8JsEngine(), + Priority = 10 + }, "ClearScriptV8"); + } + } +} diff --git a/src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8InitialisationException.cs b/src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8InitialisationException.cs new file mode 100644 index 000000000..524f954a7 --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8InitialisationException.cs @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; +using System.Runtime.Serialization; +using React.Exceptions; + +namespace React.JavaScriptEngine.ClearScriptV8 +{ + /// + /// Thrown when the JavaScript engine does not support JSX transformation + /// + [Serializable] + public class ClearScriptV8InitialisationException : ReactException + { + /// + /// Initializes a new instance of the class. + /// + public ClearScriptV8InitialisationException(string innerMessage) : + base(GetMessage(innerMessage)) { } + + /// + /// Used by deserialization + /// + protected ClearScriptV8InitialisationException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + + /// + /// Gets a message that describes the current exception. + /// + private static string GetMessage(string innerMessage) + { + return + "Failed to initialise ClearScript V8. This is most likely caused by the native libraries " + + "(ClearScriptV8-64.dll and v8-x64.dll) missing from your app's Bin directory. Please " + + "ensure your app is referencing the JavaScriptEngineSwitcher.V8 NuGet package.\n\n" + + "More details: " + innerMessage; + } + } +} diff --git a/src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8Utils.cs b/src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8Utils.cs new file mode 100644 index 000000000..fac0ec8cb --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8Utils.cs @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; +using JavaScriptEngineSwitcher.V8; +using React.Exceptions; + +namespace React.JavaScriptEngine.ClearScriptV8 +{ + /// + /// Utility methods for VroomJs JavaScript engine + /// + public static class ClearScriptV8Utils + { + /// + /// Determines if the current environment supports the ClearScript V8 engine + /// + /// + public static bool IsEnvironmentSupported() + { + return Environment.OSVersion.Platform == PlatformID.Win32NT; + } + + /// + /// If the user is explicitly referencing this assembly, they probably want to use it. + /// Attempt to use the engine and throw an exception if it doesn't work. + /// + public static void EnsureEngineFunctional() + { + int result = 0; + try + { + using (var engine = new V8JsEngine()) + { + result = engine.Evaluate("1 + 1"); + } + } + catch (Exception ex) + { + throw new ClearScriptV8InitialisationException(ex.Message); + } + + if (result != 2) + { + throw new ReactException("Mathematics is broken. 1 + 1 = " + result); + } + } + } +} diff --git a/src/React.JavaScriptEngine.ClearScriptV8/Properties/AssemblyInfo.cs b/src/React.JavaScriptEngine.ClearScriptV8/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..17e33a059 --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/Properties/AssemblyInfo.cs @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("React.JavaScriptEngine.ClearScriptV8")] +[assembly: AssemblyDescription("ClearScript V8 JavaScript library for ReactJS.NET")] +[assembly: ComVisible(false)] +[assembly: Guid("fcc41d93-09d7-4053-8d87-da2b9d0c7030")] \ No newline at end of file diff --git a/src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.csproj b/src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.csproj new file mode 100644 index 000000000..9113ed1ac --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09} + Library + Properties + React.JavaScriptEngine.ClearScriptV8 + React.JavaScriptEngine.ClearScriptV8 + v4.0 + 512 + + + + true + full + false + ..\..\bin\Debug\React.JavaScriptEngine.ClearScriptV8\ + DEBUG;TRACE + prompt + 4 + 1607 + ..\..\bin\Debug\React.JavaScriptEngine.ClearScriptV8\React.JavaScriptEngine.ClearScriptV8.XML + + + pdbonly + true + ..\..\bin\Release\React.JavaScriptEngine.ClearScriptV8\ + TRACE + prompt + 4 + 1607 + true + ..\..\bin\Release\React.JavaScriptEngine.ClearScriptV8\React.JavaScriptEngine.ClearScriptV8.XML + + + + ..\packages\JavaScriptEngineSwitcher.V8.1.2.1\lib\net40\ClearScript.dll + + + ..\packages\JavaScriptEngineSwitcher.Core.1.2.0\lib\net40\JavaScriptEngineSwitcher.Core.dll + + + ..\packages\JavaScriptEngineSwitcher.V8.1.2.1\lib\net40\JavaScriptEngineSwitcher.V8.dll + + + + + + + + Properties\SharedAssemblyInfo.cs + + + Properties\SharedAssemblyVersionInfo.cs + + + + + + + + + + + + + {d0cc8a22-cee6-485c-924b-1f94426fea59} + React + + + + + \ No newline at end of file diff --git a/src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.nutrans b/src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.nutrans new file mode 100644 index 000000000..0640b1c44 --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/React.JavaScriptEngine.ClearScriptV8.nutrans @@ -0,0 +1,21 @@ + + + + + ReactJS.NET - ClearScript V8 + V8 support for ReactJS.NET, using ClearScript. Install this package in addition to the regular ReactJS.NET packages (eg. React.Web.Mvc4 for ASP.NET MVC 4 integration). Only works on Windows, See React.JavaScriptEngine.VroomJs for a Mono (Linux and Mac OS X) version +Please refer to project site (http://reactjs.net/) for more details, usage examples and sample code. + + V8 support for ReactJS.NET + asp.net mvc asp jquery javascript js react facebook reactjs v8 clearscript + + diff --git a/src/React.JavaScriptEngine.ClearScriptV8/packages.config b/src/React.JavaScriptEngine.ClearScriptV8/packages.config new file mode 100644 index 000000000..d070a0217 --- /dev/null +++ b/src/React.JavaScriptEngine.ClearScriptV8/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/React.JavaScriptEngine.VroomJs/React.JavaScriptEngine.VroomJs.csproj b/src/React.JavaScriptEngine.VroomJs/React.JavaScriptEngine.VroomJs.csproj index f445dd9a9..4c7e43490 100644 --- a/src/React.JavaScriptEngine.VroomJs/React.JavaScriptEngine.VroomJs.csproj +++ b/src/React.JavaScriptEngine.VroomJs/React.JavaScriptEngine.VroomJs.csproj @@ -41,8 +41,9 @@ ..\Key.snk
- - ..\packages\JavaScriptEngineSwitcher.Core.1.1.3\lib\net40\JavaScriptEngineSwitcher.Core.dll + + False + ..\packages\JavaScriptEngineSwitcher.Core.1.2.0\lib\net40\JavaScriptEngineSwitcher.Core.dll ..\packages\Newtonsoft.Json.5.0.4\lib\net40\Newtonsoft.Json.dll diff --git a/src/React.JavaScriptEngine.VroomJs/packages.config b/src/React.JavaScriptEngine.VroomJs/packages.config index 1f1ecd502..1748e8ead 100644 --- a/src/React.JavaScriptEngine.VroomJs/packages.config +++ b/src/React.JavaScriptEngine.VroomJs/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj b/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj index 07c1d2e04..059fc3c60 100644 --- a/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj +++ b/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj @@ -45,6 +45,18 @@ ..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll + + False + ..\packages\JavaScriptEngineSwitcher.V8.1.2.1\lib\net40\ClearScript.dll + + + False + ..\packages\JavaScriptEngineSwitcher.Core.1.2.0\lib\net40\JavaScriptEngineSwitcher.Core.dll + + + False + ..\packages\JavaScriptEngineSwitcher.V8.1.2.1\lib\net40\JavaScriptEngineSwitcher.V8.dll + ..\packages\Newtonsoft.Json.5.0.4\lib\net40\Newtonsoft.Json.dll @@ -124,6 +136,18 @@ + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + @@ -136,6 +160,10 @@ + + {7fbe07b3-c7ba-48da-8b53-0db0bb82da09} + React.JavaScriptEngine.ClearScriptV8 + {b4a5902a-70e2-4fa4-817d-dcc78d31e9b9} React.JavaScriptEngine.VroomJs diff --git a/src/React.Sample.Mvc4/Web.config b/src/React.Sample.Mvc4/Web.config index c3df22cba..21102e147 100644 --- a/src/React.Sample.Mvc4/Web.config +++ b/src/React.Sample.Mvc4/Web.config @@ -50,6 +50,10 @@ + + + + diff --git a/src/React.Sample.Mvc4/packages.config b/src/React.Sample.Mvc4/packages.config index df80bfa5d..cad7f4a43 100644 --- a/src/React.Sample.Mvc4/packages.config +++ b/src/React.Sample.Mvc4/packages.config @@ -1,6 +1,8 @@  + + diff --git a/src/React.Tests/React.Tests.csproj b/src/React.Tests/React.Tests.csproj index a7f05afaa..9bfe7736c 100644 --- a/src/React.Tests/React.Tests.csproj +++ b/src/React.Tests/React.Tests.csproj @@ -40,9 +40,9 @@ ..\Key.snk
- + False - ..\packages\JavaScriptEngineSwitcher.Core.1.1.3\lib\net40\JavaScriptEngineSwitcher.Core.dll + ..\packages\JavaScriptEngineSwitcher.Core.1.2.0\lib\net40\JavaScriptEngineSwitcher.Core.dll False diff --git a/src/React.Tests/packages.config b/src/React.Tests/packages.config index 6ed66754a..122509889 100644 --- a/src/React.Tests/packages.config +++ b/src/React.Tests/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/React.sln b/src/React.sln index accb7c363..c4838e71c 100644 --- a/src/React.sln +++ b/src/React.sln @@ -51,6 +51,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F2875D EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "React.JavaScriptEngine.VroomJs", "React.JavaScriptEngine.VroomJs\React.JavaScriptEngine.VroomJs.csproj", "{B4A5902A-70E2-4FA4-817D-DCC78D31E9B9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "React.JavaScriptEngine.ClearScriptV8", "React.JavaScriptEngine.ClearScriptV8\React.JavaScriptEngine.ClearScriptV8.csproj", "{7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,6 +103,10 @@ Global {B4A5902A-70E2-4FA4-817D-DCC78D31E9B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {B4A5902A-70E2-4FA4-817D-DCC78D31E9B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {B4A5902A-70E2-4FA4-817D-DCC78D31E9B9}.Release|Any CPU.Build.0 = Release|Any CPU + {7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -117,5 +123,6 @@ Global {6B05AF58-BA85-45A9-97AE-88B6B21317D7} = {681C45FB-103C-48BC-B992-20C5B6B78F92} {AF531A37-B93F-4113-9C2C-4DB28064B926} = {681C45FB-103C-48BC-B992-20C5B6B78F92} {B4A5902A-70E2-4FA4-817D-DCC78D31E9B9} = {681C45FB-103C-48BC-B992-20C5B6B78F92} + {7FBE07B3-C7BA-48DA-8B53-0DB0BB82DA09} = {681C45FB-103C-48BC-B992-20C5B6B78F92} EndGlobalSection EndGlobal diff --git a/src/React/AssemblyRegistration.cs b/src/React/AssemblyRegistration.cs index f6ddcd7bb..a3411c468 100644 --- a/src/React/AssemblyRegistration.cs +++ b/src/React/AssemblyRegistration.cs @@ -45,12 +45,12 @@ public void Register(TinyIoCContainer container) container.Register(new JavaScriptEngineFactory.Registration { Factory = () => new MsieJsEngine(new MsieConfiguration { EngineMode = JsEngineMode.ChakraActiveScript }), - Priority = 10 + Priority = 20 }, "MsieChakra"); container.Register(new JavaScriptEngineFactory.Registration { Factory = () => new MsieJsEngine(new MsieConfiguration { EngineMode = JsEngineMode.Classic }), - Priority = 20 + Priority = 30 }, "MsieClassic"); container.Register(new JavaScriptEngineFactory.Registration { diff --git a/src/React/React.csproj b/src/React/React.csproj index 8aadeb367..a8201d87e 100644 --- a/src/React/React.csproj +++ b/src/React/React.csproj @@ -46,25 +46,25 @@ - + False - ..\packages\JavaScriptEngineSwitcher.Core.1.1.3\lib\net40\JavaScriptEngineSwitcher.Core.dll + ..\packages\JavaScriptEngineSwitcher.Core.1.2.0\lib\net40\JavaScriptEngineSwitcher.Core.dll - - False - ..\packages\JavaScriptEngineSwitcher.Jint.1.1.12\lib\net40\JavaScriptEngineSwitcher.Jint.dll + + ..\packages\JavaScriptEngineSwitcher.Jint.1.2.1\lib\net40\JavaScriptEngineSwitcher.Jint.dll + True - - False - ..\packages\JavaScriptEngineSwitcher.Msie.1.1.11\lib\net40\JavaScriptEngineSwitcher.Msie.dll + + ..\packages\JavaScriptEngineSwitcher.Msie.1.2.0\lib\net40\JavaScriptEngineSwitcher.Msie.dll + True - - False - ..\packages\JavaScriptEngineSwitcher.Jint.1.1.12\lib\net40\Jint.dll + + ..\packages\JavaScriptEngineSwitcher.Jint.1.2.1\lib\net40\Jint.dll + True - - False - ..\packages\MsieJavaScriptEngine.1.4.4\lib\net40\MsieJavaScriptEngine.dll + + ..\packages\MsieJavaScriptEngine.1.5.0\lib\net40\MsieJavaScriptEngine.dll + True False diff --git a/src/React/packages.config b/src/React/packages.config index 952c5526c..8af52c8f0 100644 --- a/src/React/packages.config +++ b/src/React/packages.config @@ -1,8 +1,8 @@  - - - - + + + + \ No newline at end of file From b710d1a39aef74df653014594f0be2bd2994ed09 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 2 Nov 2014 21:36:42 -0800 Subject: [PATCH 013/859] Blog post for 1.1.3 release --- site/jekyll/_posts/2014-11-02-1.1.3-release.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 site/jekyll/_posts/2014-11-02-1.1.3-release.md diff --git a/site/jekyll/_posts/2014-11-02-1.1.3-release.md b/site/jekyll/_posts/2014-11-02-1.1.3-release.md new file mode 100644 index 000000000..ae3ad9a51 --- /dev/null +++ b/site/jekyll/_posts/2014-11-02-1.1.3-release.md @@ -0,0 +1,16 @@ +--- +title: "ReactJS.NET 1.1.3 - React 0.12 and V8 on Windows" +layout: post +author: Daniel Lo Nigro +--- + +I'm happy to announce the release of ReactJS.NET 1.1.3! + +In this release, the React version has been upgraded from 0.11.1 to 0.12. The main changes in this React release are that `/** @jsx React.DOM */` is no longer required at the top of your JSX files, and `React.renderComponent` is now `React.render`. A few other API methods have changed as well. [See the official announcement](http://facebook.github.io/react/blog/2014/10/28/react-v0.12.html) for more information on all the changes in this version of React. + +ReactJS.NET 1.1.3 also adds preliminary support for using Google's V8 JavaScript engine on Windows, via Microsoft's [ClearScript](https://clearscript.codeplex.com/) project. Previously ReactJS.NET only supported V8 on Mac OS X and Linux. This should be considered beta quality, in that it works but has not been stress tested in a production environment yet (whereas the Linux implementation has been). Future releases will make the handling of JavaScript engines more efficient. + +Have fun, and as always, please feel free to send feedback or bug reports +[on GitHub](https://github.com/reactjs/React.NET). + +— Daniel From 7993912b6e20f0105f4edf50f4b651adde83712b Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 2 Nov 2014 21:24:33 -0800 Subject: [PATCH 014/859] Add ability to return a source map from JsxTransformer. --- src/React/IJsxTransformer.cs | 15 +++++-- src/React/JavaScriptWithSourceMap.cs | 31 ++++++++++++++ src/React/JsxTransformer.cs | 26 +++++++++++- src/React/React.csproj | 2 + src/React/ReactEnvironment.cs | 23 ++++++++++- src/React/Resources/shims.js | 22 ++++++++++ src/React/SourceMap.cs | 61 ++++++++++++++++++++++++++++ 7 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 src/React/JavaScriptWithSourceMap.cs create mode 100644 src/React/SourceMap.cs diff --git a/src/React/IJsxTransformer.cs b/src/React/IJsxTransformer.cs index 7764b88c8..1c4e557b3 100644 --- a/src/React/IJsxTransformer.cs +++ b/src/React/IJsxTransformer.cs @@ -18,7 +18,7 @@ public interface IJsxTransformer /// Transforms a JSX file. Results of the JSX to JavaScript transformation are cached. ///
/// Name of the file to load - /// true if support for es6 syntax should be rewritten. + /// true if support for ES6 syntax should be enabled /// JavaScript string TransformJsxFile(string filename, bool? useHarmony = null); @@ -27,16 +27,25 @@ public interface IJsxTransformer /// if loading from a file since this will cache the result. ///
/// JSX - /// true if support for es6 syntax should be rewritten. + /// true if support for ES6 syntax should be enabled /// JavaScript string TransformJsx(string input, bool? useHarmony = null); + + /// + /// Transforms JSX to regular JavaScript and also returns a source map to map the compiled + /// source to the original version. The result is not cached. + /// + /// JSX + /// true if support for ES6 syntax should be enabled + /// JavaScript and source map + JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony); /// /// Transforms a JSX file to JavaScript, and saves the result into a ".generated.js" file /// alongside the original file. /// /// Name of the file to load - /// true if support for es6 syntax should be rewritten. + /// true if support for ES6 syntax should be enabled /// File contents string TransformAndSaveJsxFile(string filename, bool? useHarmony = null); diff --git a/src/React/JavaScriptWithSourceMap.cs b/src/React/JavaScriptWithSourceMap.cs new file mode 100644 index 000000000..ed81b50d6 --- /dev/null +++ b/src/React/JavaScriptWithSourceMap.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; + +namespace React +{ + /// + /// Represents the result of a JSX to JavaScript transformation along with its + /// corresponding source map. + /// + [Serializable] + public class JavaScriptWithSourceMap + { + /// + /// The transformed result + /// + public string Code { get; set; } + + /// + /// The source map for this code + /// + public SourceMap SourceMap { get; set; } + } +} diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index 2a5ccd4bb..c65dd39ec 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -147,7 +147,7 @@ public string TransformJsx(string input, bool? useHarmony = null) var output = _environment.ExecuteWithLargerStackIfRequired( "ReactNET_transform", input, - useHarmony.HasValue ? useHarmony.Value : _config.UseHarmony + useHarmony ?? _config.UseHarmony ); return output; } @@ -157,6 +157,30 @@ public string TransformJsx(string input, bool? useHarmony = null) } } + /// + /// Transforms JSX to regular JavaScript and also returns a source map to map the compiled + /// source to the original version. The result is not cached. + /// + /// JSX + /// true if support for ES6 syntax should be enabled + /// JavaScript and source map + public JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony) + { + EnsureJsxTransformerSupported(); + try + { + return _environment.ExecuteWithLargerStackIfRequired( + "ReactNET_transform_sourcemap", + input, + useHarmony ?? _config.UseHarmony + ); + } + catch (Exception ex) + { + throw new JsxException(ex.Message, ex); + } + } + /// /// Gets the header prepended to JSX transformed files. Contains a hash that is used to /// validate the cache. diff --git a/src/React/React.csproj b/src/React/React.csproj index a8201d87e..7b09c1882 100644 --- a/src/React/React.csproj +++ b/src/React/React.csproj @@ -85,6 +85,8 @@ + + diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index 9a30b86a0..354497f44 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -14,6 +14,8 @@ using System.Text; using System.Threading; using JavaScriptEngineSwitcher.Core; +using JavaScriptEngineSwitcher.Core.Helpers; +using Newtonsoft.Json; using React.Exceptions; namespace React @@ -231,7 +233,26 @@ public T Execute(string function, params object[] args) { try { - return Engine.CallFunction(function, args); + if (ValidationHelpers.IsSupportedType(typeof (T))) + { + // Type is supported directly (ie. a scalar type like string/int/bool) + // Just execute the function directly. + return Engine.CallFunction(function, args); + } + // The type is not a scalar type. Assume the function will return its result as + // JSON. + var resultJson = Engine.CallFunction(function, args); + try + { + return JsonConvert.DeserializeObject(resultJson); + } + catch (JsonReaderException ex) + { + throw new ReactException(string.Format( + "{0} did not return valid JSON: {1}.\n\n{2}", + function, ex.Message, resultJson + )); + } } catch (JsRuntimeException ex) { diff --git a/src/React/Resources/shims.js b/src/React/Resources/shims.js index 568a7691e..f8379a65e 100644 --- a/src/React/Resources/shims.js +++ b/src/React/Resources/shims.js @@ -25,4 +25,26 @@ function ReactNET_transform(input, harmony) { } catch (ex) { throw new Error(ex.message + " (at line " + ex.lineNumber + " column " + ex.column + ")"); } +} + +function ReactNET_transform_sourcemap(input, harmony) { + try { + var result = global.JSXTransformer.transform(input, { + harmony: !!harmony, + sourceMap: true + }); + if (!result.sourceMap) { + return JSON.stringify({ + code: result.code, + sourceMap: null + }); + } + + return JSON.stringify({ + code: result.code, + sourceMap: result.sourceMap.toJSON() + }); + } catch (ex) { + throw new Error(ex.message + " (at line " + ex.lineNumber + " column " + ex.column + ")"); + } } \ No newline at end of file diff --git a/src/React/SourceMap.cs b/src/React/SourceMap.cs new file mode 100644 index 000000000..b17c9da6b --- /dev/null +++ b/src/React/SourceMap.cs @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; +using System.Collections.Generic; + +namespace React +{ + /// + /// Represents the data contained in a source map + /// + [Serializable] + public class SourceMap + { + /// + /// Version number of the source map spec used to build this source map. Expected + /// to be version 3. + /// + public int Version { get; set; } + + /// + /// An optional name of the generated code that this source map is associated with. + /// + public string File { get; set; } + + /// + /// An optional source root, useful for relocating source files on a server or + /// removing repeated values in the entry. This value is + /// prepended to the individual entries in the field. + /// + public string SourceRoot { get; set; } + + /// + /// A list of original sources used by the entry. + /// + public IList Sources { get; set; } + + /// + /// An optional list of source content, useful when the can't + /// be hosted. The contents are listed in the same order as the . + /// null may be used if some original sources should be retrieved by name. + /// + public IList SourcesContent { get; set; } + + /// + /// A list of symbol names used by the entry. + /// + public IList Names { get; set; } + + /// + /// A string with the mapping data encoded in base 64 VLQ. + /// + public string Mappings { get; set; } + } +} From c2f7cf669e8add6e94534f11273c2020ddd96ff0 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Wed, 5 Nov 2014 12:10:09 +0100 Subject: [PATCH 015/859] Add possibility to set own JsonSerializerSettings in confiquration This affects how components are rendered server-side. --- src/React/IReactEnvironment.cs | 5 +++++ src/React/IReactSiteConfiguration.cs | 16 ++++++++++++++++ src/React/ReactComponent.cs | 4 ++-- src/React/ReactEnvironment.cs | 8 ++++++++ src/React/ReactSiteConfiguration.cs | 27 ++++++++++++++++++++++++++- 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/React/IReactEnvironment.cs b/src/React/IReactEnvironment.cs index b14f8b2ff..126f5e1a4 100644 --- a/src/React/IReactEnvironment.cs +++ b/src/React/IReactEnvironment.cs @@ -107,5 +107,10 @@ public interface IReactEnvironment /// Gets the JSX Transformer for this environment. /// IJsxTransformer JsxTransformer { get; } + + /// + /// Gets the global site configuration. + /// + IReactSiteConfiguration Configuration { get; } } } diff --git a/src/React/IReactSiteConfiguration.cs b/src/React/IReactSiteConfiguration.cs index 43c1db33b..bbc3cf4f2 100644 --- a/src/React/IReactSiteConfiguration.cs +++ b/src/React/IReactSiteConfiguration.cs @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +using Newtonsoft.Json; using System.Collections.Generic; namespace React @@ -42,5 +43,20 @@ public interface IReactSiteConfiguration /// Specifies whether ES6 (harmony) syntax should be transformed ///
IReactSiteConfiguration SetUseHarmony(bool useHarmony); + + /// + /// Gets the configuration for json serializer. + /// + JsonSerializerSettings JsonSerializerSettings { get; } + + /// + /// Sets the configuration for json serializer. + /// + /// + /// Thic confiquration is used when component initialization script + /// is being generated server-side. + /// + /// The settings. + IReactSiteConfiguration SetJsonSerializerSettings(JsonSerializerSettings settings); } } diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index 7332c765e..454680932 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -103,7 +103,7 @@ public string RenderJavaScript() return string.Format( "React.render({0}, document.getElementById({1}))", GetComponentInitialiser(), - JsonConvert.SerializeObject(_containerId) + JsonConvert.SerializeObject(_containerId, _environment.Configuration.JsonSerializerSettings) ); } @@ -133,7 +133,7 @@ private void EnsureComponentExists() /// JavaScript for component initialisation private string GetComponentInitialiser() { - var encodedProps = JsonConvert.SerializeObject(Props); + var encodedProps = JsonConvert.SerializeObject(Props, _environment.Configuration.JsonSerializerSettings); return string.Format( "{0}({1})", _componentName, diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index 9a30b86a0..d86e1ca4f 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -412,5 +412,13 @@ private JsRuntimeException WrapJavaScriptRuntimeException(JsRuntimeException ex) Source = ex.Source, }; } + + /// + /// Gets the site-wide configuration. + /// + public IReactSiteConfiguration Configuration + { + get { return _config; } + } } } diff --git a/src/React/ReactSiteConfiguration.cs b/src/React/ReactSiteConfiguration.cs index 9ed0ea128..5abcb3a4e 100644 --- a/src/React/ReactSiteConfiguration.cs +++ b/src/React/ReactSiteConfiguration.cs @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +using Newtonsoft.Json; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -32,6 +33,8 @@ static ReactSiteConfiguration() ///
private readonly IList _scriptFiles = new List(); + private JsonSerializerSettings _jsonSerializerSettings; + /// /// Adds a script to the list of scripts that are executed. This should be called for all /// React components and their dependencies. @@ -60,7 +63,7 @@ public IList Scripts /// /// true if support for es6 syntax should be rewritten. public bool UseHarmony { get; set; } - + /// /// Specifies whether ES6 (harmony) syntax should be transformed /// @@ -69,5 +72,27 @@ public IReactSiteConfiguration SetUseHarmony(bool useHarmony) UseHarmony = useHarmony; return this; } + + /// + /// Gets the configuration for json serializer. + /// + public JsonSerializerSettings JsonSerializerSettings + { + get { return _jsonSerializerSettings; } + } + + /// + /// Sets the configuration for json serializer. + /// + /// Settings. + /// + /// Thic confiquration is used when component initialization script + /// is being generated server-side. + /// + public IReactSiteConfiguration SetJsonSerializerSettings(JsonSerializerSettings settings) + { + _jsonSerializerSettings = settings; + return this; + } } } From 32d84708f6e367b0683af0c3dedf3f2d9fbf2f17 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Thu, 6 Nov 2014 12:14:40 +0100 Subject: [PATCH 016/859] Pass IReactSiteConfiguration as parameter for ReactComponent ctor --- src/React.Tests/Core/ReactComponentTest.cs | 11 +++++++---- src/React/IReactEnvironment.cs | 5 ----- src/React/IReactSiteConfiguration.cs | 4 ++-- src/React/ReactComponent.cs | 13 ++++++++++--- src/React/ReactEnvironment.cs | 2 +- src/React/ReactSiteConfiguration.cs | 9 ++++----- 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/React.Tests/Core/ReactComponentTest.cs b/src/React.Tests/Core/ReactComponentTest.cs index 73f7b2e1b..1d4103aad 100644 --- a/src/React.Tests/Core/ReactComponentTest.cs +++ b/src/React.Tests/Core/ReactComponentTest.cs @@ -21,7 +21,7 @@ public void RenderHtmlShouldThrowExceptionIfComponentDoesNotExist() { var environment = new Mock(); environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(false); - var component = new ReactComponent(environment.Object, "Foo", "container"); + var component = new ReactComponent(environment.Object, null, "Foo", "container"); Assert.Throws(() => { @@ -34,8 +34,9 @@ public void RenderHtmlShouldCallRenderComponent() { var environment = new Mock(); environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); + var config = new Mock(); - var component = new ReactComponent(environment.Object, "Foo", "container") + var component = new ReactComponent(environment.Object, config.Object, "Foo", "container") { Props = new { hello = "World" } }; @@ -51,8 +52,9 @@ public void RenderHtmlShouldWrapComponentInDiv() environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); environment.Setup(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")) .Returns("[HTML]"); + var config = new Mock(); - var component = new ReactComponent(environment.Object, "Foo", "container") + var component = new ReactComponent(environment.Object, config.Object, "Foo", "container") { Props = new { hello = "World" } }; @@ -65,8 +67,9 @@ public void RenderHtmlShouldWrapComponentInDiv() public void RenderJavaScriptShouldCallRenderComponent() { var environment = new Mock(); + var config = new Mock(); - var component = new ReactComponent(environment.Object, "Foo", "container") + var component = new ReactComponent(environment.Object, config.Object, "Foo", "container") { Props = new { hello = "World" } }; diff --git a/src/React/IReactEnvironment.cs b/src/React/IReactEnvironment.cs index 126f5e1a4..b14f8b2ff 100644 --- a/src/React/IReactEnvironment.cs +++ b/src/React/IReactEnvironment.cs @@ -107,10 +107,5 @@ public interface IReactEnvironment /// Gets the JSX Transformer for this environment. ///
IJsxTransformer JsxTransformer { get; } - - /// - /// Gets the global site configuration. - /// - IReactSiteConfiguration Configuration { get; } } } diff --git a/src/React/IReactSiteConfiguration.cs b/src/React/IReactSiteConfiguration.cs index bbc3cf4f2..3ae60b03a 100644 --- a/src/React/IReactSiteConfiguration.cs +++ b/src/React/IReactSiteConfiguration.cs @@ -45,9 +45,9 @@ public interface IReactSiteConfiguration IReactSiteConfiguration SetUseHarmony(bool useHarmony); /// - /// Gets the configuration for json serializer. + /// Gets or sets the configuration for JSON serializer. /// - JsonSerializerSettings JsonSerializerSettings { get; } + JsonSerializerSettings JsonSerializerSettings { get; set; } /// /// Sets the configuration for json serializer. diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index 454680932..a7c0ebd96 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -32,6 +32,11 @@ public class ReactComponent : IReactComponent /// private readonly IReactEnvironment _environment; + /// + /// Global site configuration + /// + private readonly IReactSiteConfiguration _configuration; + /// /// Name of the component /// @@ -51,12 +56,14 @@ public class ReactComponent : IReactComponent /// Initializes a new instance of the class. /// /// The environment. + /// Site-wide configuration. /// Name of the component. /// The ID of the container DIV for this component - public ReactComponent(IReactEnvironment environment, string componentName, string containerId) + public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration configuration, string componentName, string containerId) { EnsureComponentNameValid(componentName); _environment = environment; + _configuration = configuration; _componentName = componentName; _containerId = containerId; } @@ -103,7 +110,7 @@ public string RenderJavaScript() return string.Format( "React.render({0}, document.getElementById({1}))", GetComponentInitialiser(), - JsonConvert.SerializeObject(_containerId, _environment.Configuration.JsonSerializerSettings) + JsonConvert.SerializeObject(_containerId, _configuration.JsonSerializerSettings) // SerializeObject accepts null settings ); } @@ -133,7 +140,7 @@ private void EnsureComponentExists() /// JavaScript for component initialisation private string GetComponentInitialiser() { - var encodedProps = JsonConvert.SerializeObject(Props, _environment.Configuration.JsonSerializerSettings); + var encodedProps = JsonConvert.SerializeObject(Props, _configuration.JsonSerializerSettings); // SerializeObject accepts null settings return string.Format( "{0}({1})", _componentName, diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index d86e1ca4f..46fde07d0 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -268,7 +268,7 @@ public IReactComponent CreateComponent(string componentName, T props) EnsureUserScriptsLoaded(); _maxContainerId++; var containerId = string.Format(CONTAINER_ELEMENT_NAME, _maxContainerId); - var component = new ReactComponent(this, componentName, containerId) + var component = new ReactComponent(this, _config, componentName, containerId) { Props = props }; diff --git a/src/React/ReactSiteConfiguration.cs b/src/React/ReactSiteConfiguration.cs index 5abcb3a4e..ec65b10f0 100644 --- a/src/React/ReactSiteConfiguration.cs +++ b/src/React/ReactSiteConfiguration.cs @@ -33,8 +33,6 @@ static ReactSiteConfiguration() /// private readonly IList _scriptFiles = new List(); - private JsonSerializerSettings _jsonSerializerSettings; - /// /// Adds a script to the list of scripts that are executed. This should be called for all /// React components and their dependencies. @@ -74,11 +72,12 @@ public IReactSiteConfiguration SetUseHarmony(bool useHarmony) } /// - /// Gets the configuration for json serializer. + /// Gets or sets the configuration for JSON serializer. /// public JsonSerializerSettings JsonSerializerSettings { - get { return _jsonSerializerSettings; } + get; + set; } /// @@ -91,7 +90,7 @@ public JsonSerializerSettings JsonSerializerSettings /// public IReactSiteConfiguration SetJsonSerializerSettings(JsonSerializerSettings settings) { - _jsonSerializerSettings = settings; + JsonSerializerSettings = settings; return this; } } From 272d8b6893a94e2d1e84e91bf9326aeda1fc77fe Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 8 Nov 2014 00:06:47 -0800 Subject: [PATCH 017/859] Support source maps in JsxHandler --- src/React.Sample.Mvc4/Content/Sample.jsx | 10 +- src/React.Tests/Core/JsxTransformerTests.cs | 65 +++---- src/React.Web/AspNetCache.cs | 48 ++--- src/React.Web/JsxHandler.cs | 68 ++++++- src/React.Web/React.Web.csproj | 3 + src/React.Web/packages.config | 1 + src/React/FileCacheHash.cs | 2 +- src/React/ICache.cs | 23 ++- src/React/IJsxTransformer.cs | 23 ++- src/React/JavaScriptWithSourceMap.cs | 5 + src/React/JsxTransformer.cs | 192 ++++++++++++++++---- src/React/NullCache.cs | 24 ++- src/React/SourceMap.cs | 25 +++ 13 files changed, 361 insertions(+), 128 deletions(-) diff --git a/src/React.Sample.Mvc4/Content/Sample.jsx b/src/React.Sample.Mvc4/Content/Sample.jsx index fa3e1c4ff..ecf014f96 100644 --- a/src/React.Sample.Mvc4/Content/Sample.jsx +++ b/src/React.Sample.Mvc4/Content/Sample.jsx @@ -30,21 +30,21 @@ var CommentsBox = React.createClass({ var url = evt.target.href; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); - xhr.onload = function() { + xhr.onload = () => { var data = JSON.parse(xhr.responseText); this.setState({ comments: this.state.comments.concat(data.comments), hasMore: data.hasMore, loadingMore: false }); - }.bind(this); + }; xhr.send(); return false; }, render() { - var commentNodes = this.state.comments.map(function (comment) { - return {comment.Text}; - }); + var commentNodes = this.state.comments.map(comment => + {comment.Text} + ); return (
diff --git a/src/React.Tests/Core/JsxTransformerTests.cs b/src/React.Tests/Core/JsxTransformerTests.cs index b7c46bc0a..1b4358f57 100644 --- a/src/React.Tests/Core/JsxTransformerTests.cs +++ b/src/React.Tests/Core/JsxTransformerTests.cs @@ -8,7 +8,6 @@ */ using System; -using System.Collections.Generic; using Moq; using NUnit.Framework; using React.Exceptions; @@ -21,7 +20,7 @@ public class JsxTransformerTests private Mock _environment; private Mock _cache; private Mock _fileSystem; - private Mock _fileCacheHash; + private Mock _fileCacheHash; private JsxTransformer _jsxTransformer; [SetUp] @@ -85,13 +84,10 @@ public void ShouldThrowIfEngineNotSupported() [Test] public void ShouldUseCacheProvider() { - _cache.Setup(x => x.GetOrInsert( - /*key*/ "JSX_foo.jsx", - /*slidingExpiration*/ It.IsAny(), - /*getData*/ It.IsAny>(), - /*cacheDependencyKeys*/ It.IsAny>(), - /*cacheDependencyFiles*/ It.IsAny>() - )).Returns("/* cached */"); + _cache.Setup(x => x.Get("JSX_v2_foo.jsx", null)).Returns(new JavaScriptWithSourceMap + { + Code = "/* cached */" + }); var result = _jsxTransformer.TransformJsxFile("foo.jsx"); Assert.AreEqual("/* cached */", result); @@ -104,7 +100,7 @@ public void ShouldUseFileSystemCacheIfHashValid() _fileSystem.Setup(x => x.FileExists("foo.generated.js")).Returns(true); _fileSystem.Setup(x => x.ReadAsString("foo.generated.js")).Returns("/* filesystem cached */"); _fileCacheHash.Setup(x => x.ValidateHash(It.IsAny(), It.IsAny())).Returns(true); - + var result = _jsxTransformer.TransformJsxFile("foo.jsx"); Assert.AreEqual("/* filesystem cached */", result); } @@ -117,13 +113,14 @@ public void ShouldTransformJsxIfFileCacheHashInvalid() _fileSystem.Setup(x => x.ReadAsString("foo.generated.js")).Returns("/* filesystem cached invalid */"); _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); _fileCacheHash.Setup(x => x.ValidateHash(It.IsAny(), It.IsAny())).Returns(false); - - _jsxTransformer.TransformJsxFile("foo.jsx"); - _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( - "ReactNET_transform", - "
Hello World
", + _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( + "ReactNET_transform_sourcemap", + It.IsAny(), false - )); + )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); + + var result = _jsxTransformer.TransformJsxFile("foo.jsx"); + Assert.AreEqual("React.DOM.div('Hello World')", result); } [Test] @@ -132,24 +129,25 @@ public void ShouldTransformJsxIfNoCache() SetUpEmptyCache(); _fileSystem.Setup(x => x.FileExists("foo.generated.js")).Returns(false); _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); - - _jsxTransformer.TransformJsxFile("foo.jsx"); - _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( - "ReactNET_transform", - "
Hello World
", + _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( + "ReactNET_transform_sourcemap", + It.IsAny(), false - )); + )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); + + var result = _jsxTransformer.TransformJsxFile("foo.jsx"); + Assert.AreEqual("React.DOM.div('Hello World')", result); } [Test] public void ShouldSaveTransformationResult() { _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); - _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( - "ReactNET_transform", - "
Hello World
", + _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( + "ReactNET_transform_sourcemap", + It.IsAny(), false - )).Returns("React.DOM.div('Hello World')"); + )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); string result = null; _fileSystem.Setup(x => x.WriteAsString("foo.generated.js", It.IsAny())).Callback( @@ -163,20 +161,7 @@ public void ShouldSaveTransformationResult() private void SetUpEmptyCache() { - _cache.Setup(x => x.GetOrInsert( - /*key*/ "JSX_foo.jsx", - /*slidingExpiration*/ It.IsAny(), - /*getData*/ It.IsAny>(), - /*cacheDependencyKeys*/ It.IsAny>(), - /*cacheDependencyFiles*/ It.IsAny>() - )) - .Returns(( - string key, - TimeSpan slidingExpiration, - Func getData, - IEnumerable cacheDependencyFiles, - IEnumerable cacheDependencyKeys - ) => getData()); + _cache.Setup(x => x.Get("JSX_v2_foo.jsx", null)).Returns((JavaScriptWithSourceMap)null); } } } diff --git a/src/React.Web/AspNetCache.cs b/src/React.Web/AspNetCache.cs index 79099614c..f63900886 100644 --- a/src/React.Web/AspNetCache.cs +++ b/src/React.Web/AspNetCache.cs @@ -35,15 +35,28 @@ public AspNetCache(HttpContextBase context) } /// - /// Get an item from the cache. If it doesn't exist, call the function to load it + /// Get an item from the cache. Returns if the item does + /// not exist. /// /// Type of data - /// The cache key. + /// The cache key + /// Value to return if item is not in the cache + /// Data from cache, otherwise + public T Get(string key, T fallback = default(T)) + { + return (T)(_cache[key] ?? fallback); + } + + /// + /// Sets an item in the cache. + /// + /// Type of data + /// The cache key + /// Data to cache /// /// Sliding expiration, if cache key is not accessed in this time period it will /// automatically be removed from the cache /// - /// Function to load data to cache. Called if data isn't in the cache, or is stale /// /// Filenames this cached item is dependent on. If any of these files change, the cache /// will be cleared automatically @@ -52,31 +65,18 @@ public AspNetCache(HttpContextBase context) /// Other cache keys this cached item is dependent on. If any of these keys change, the /// cache will be cleared automatically /// - /// Data - public T GetOrInsert( - string key, - TimeSpan slidingExpiration, - Func getData, + public void Set( + string key, T data, + TimeSpan slidingExpiration, IEnumerable cacheDependencyFiles = null, IEnumerable cacheDependencyKeys = null ) { - // Check for data in cache - var data = (T)(_cache[key] ?? default(T)); - - // http://stackoverflow.com/questions/65351/null-or-default-comparsion-of-generic-argument-in-c-sharp - if (object.Equals(data, default(T))) - { - // Load data and save into cache - data = getData(); - var cacheDependency = new CacheDependency( - (cacheDependencyFiles ?? Enumerable.Empty()).ToArray(), - (cacheDependencyKeys ?? Enumerable.Empty()).ToArray() - ); - _cache.Insert(key, data, cacheDependency, Cache.NoAbsoluteExpiration, slidingExpiration); - } - - return data; + var cacheDependency = new CacheDependency( + (cacheDependencyFiles ?? Enumerable.Empty()).ToArray(), + (cacheDependencyKeys ?? Enumerable.Empty()).ToArray() + ); + _cache.Insert(key, data, cacheDependency, Cache.NoAbsoluteExpiration, slidingExpiration); } } } diff --git a/src/React.Web/JsxHandler.cs b/src/React.Web/JsxHandler.cs index 1fa1606a3..b5b4f270d 100644 --- a/src/React.Web/JsxHandler.cs +++ b/src/React.Web/JsxHandler.cs @@ -8,7 +8,6 @@ */ using System.Web; -using System.Web.Caching; namespace React.Web { @@ -46,18 +45,75 @@ HttpResponseBase response /// Executes the handler. Outputs JavaScript to the response. ///
public void Execute() + { + if (_request.QueryString["map"] != null) + { + RenderSourceMap(); + } + else + { + RenderJsxFile(); + } + } + + /// + /// Renders the JSX file converted to regular JavaScript. + /// + private void RenderJsxFile() { var relativePath = _request.Url.LocalPath; - var result = _environment.JsxTransformer.TransformJsxFile(relativePath); + var result = _environment.JsxTransformer.TransformJsxFileWithSourceMap(relativePath); + var sourceMapUri = GetSourceMapUri(relativePath, result.Hash); + ConfigureCaching(); + _response.ContentType = "text/javascript"; + // The sourcemap spec says to use SourceMap, but Firefox only accepts X-SourceMap + _response.AddHeader("SourceMap", sourceMapUri); + _response.AddHeader("X-SourceMap", sourceMapUri); - // Only cache on the server-side for now - _response.AddFileDependency(_fileSystem.MapPath(relativePath)); + _response.Write(result.Code); + } + + /// + /// Renders the source map for this JSX file. + /// + private void RenderSourceMap() + { + var relativePath = _request.Url.LocalPath; + var result = _environment.JsxTransformer.TransformJsxFileWithSourceMap(relativePath, forceGenerateSourceMap: true); + if (result.SourceMap == null) + { + _response.StatusCode = 500; + _response.StatusDescription = "Unable to generate source map"; + return; + } + var sourceMap = result.SourceMap.ToJson(); + + ConfigureCaching(); + _response.ContentType = "application/json"; + //_response.Write(")]}\n"); // Recommended by the spec but Firefox doesn't support it + _response.Write(sourceMap); + } + + /// + /// Send headers to cache the response. Only caches on the server-side for now + /// + private void ConfigureCaching() + { + _response.AddFileDependency(_fileSystem.MapPath(_request.Url.LocalPath)); _response.Cache.SetCacheability(HttpCacheability.Server); _response.Cache.SetLastModifiedFromFileDependencies(); _response.Cache.SetETagFromFileDependencies(); + } - _response.ContentType = "text/javascript"; - _response.Write(result); + /// + /// Gets the URI to the source map of the specified file + /// + /// Relative path to the JavaScript file + /// Hash of the file + /// URI to the file + private static string GetSourceMapUri(string relativePath, string hash) + { + return string.Format("{0}?map={1}", relativePath, hash); } } } diff --git a/src/React.Web/React.Web.csproj b/src/React.Web/React.Web.csproj index 3449bdb88..ab37b3cd9 100644 --- a/src/React.Web/React.Web.csproj +++ b/src/React.Web/React.Web.csproj @@ -46,6 +46,9 @@ True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + ..\packages\Newtonsoft.Json.5.0.4\lib\net40\Newtonsoft.Json.dll + diff --git a/src/React.Web/packages.config b/src/React.Web/packages.config index 193fb73d7..324986189 100644 --- a/src/React.Web/packages.config +++ b/src/React.Web/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/src/React/FileCacheHash.cs b/src/React/FileCacheHash.cs index 2a6d18cbb..59a31c905 100644 --- a/src/React/FileCacheHash.cs +++ b/src/React/FileCacheHash.cs @@ -21,7 +21,7 @@ public class FileCacheHash : IFileCacheHash /// /// Prefix used for hash line in transformed file. Used for caching. /// - internal const string HASH_PREFIX = "// @hash "; + internal const string HASH_PREFIX = "// @hash v2-"; /// /// Althorithm for calculating file hashes diff --git a/src/React/ICache.cs b/src/React/ICache.cs index 5f31d257d..6dd735f38 100644 --- a/src/React/ICache.cs +++ b/src/React/ICache.cs @@ -18,15 +18,25 @@ namespace React public interface ICache { /// - /// Get an item from the cache. If it doesn't exist, call the function to load it + /// Get an item from the cache. Returns if the item does + /// not exist. /// /// Type of data - /// The cache key. + /// The cache key + /// Value to return if item is not in the cache + /// Data from cache, otherwise + T Get(string key, T fallback = default(T)); + + /// + /// Sets an item in the cache. + /// + /// Type of data + /// The cache key + /// Data to cache /// /// Sliding expiration, if cache key is not accessed in this time period it will /// automatically be removed from the cache /// - /// Function to load data to cache. Called if data isn't in the cache, or is stale /// /// Filenames this cached item is dependent on. If any of these files change, the cache /// will be cleared automatically @@ -35,11 +45,10 @@ public interface ICache /// Other cache keys this cached item is dependent on. If any of these keys change, the /// cache will be cleared automatically /// - /// Data - T GetOrInsert( - string key, + void Set( + string key, + T data, TimeSpan slidingExpiration, - Func getData, IEnumerable cacheDependencyFiles = null, IEnumerable cacheDependencyKeys = null ); diff --git a/src/React/IJsxTransformer.cs b/src/React/IJsxTransformer.cs index 1c4e557b3..ee9b475ce 100644 --- a/src/React/IJsxTransformer.cs +++ b/src/React/IJsxTransformer.cs @@ -22,6 +22,19 @@ public interface IJsxTransformer /// JavaScript string TransformJsxFile(string filename, bool? useHarmony = null); + /// + /// Transforms a JSX file to regular JavaScript and also returns a source map to map the + /// compiled source to the original version. Results of the JSX to JavaScript + /// transformation are cached. + /// + /// Name of the file to load + /// + /// true to re-transform the file if a cached version with no source map is available + /// + /// true if support for ES6 syntax should be enabled + /// JavaScript and source map + JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bool forceGenerateSourceMap = false, bool? useHarmony = null); + /// /// Transforms JSX into regular JavaScript. The result is not cached. Use /// if loading from a file since this will cache the result. @@ -38,7 +51,7 @@ public interface IJsxTransformer /// JSX /// true if support for ES6 syntax should be enabled /// JavaScript and source map - JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony); + JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony = null); /// /// Transforms a JSX file to JavaScript, and saves the result into a ".generated.js" file @@ -55,5 +68,13 @@ public interface IJsxTransformer /// Path of the JSX file /// Output path of the compiled file string GetJsxOutputPath(string path); + + /// + /// Returns the path the specified JSX file's source map will be cached to if + /// is called. + /// + /// Path of the JSX file + /// Output path of the source map + string GetSourceMapOutputPath(string path); } } \ No newline at end of file diff --git a/src/React/JavaScriptWithSourceMap.cs b/src/React/JavaScriptWithSourceMap.cs index ed81b50d6..e41ae8005 100644 --- a/src/React/JavaScriptWithSourceMap.cs +++ b/src/React/JavaScriptWithSourceMap.cs @@ -23,6 +23,11 @@ public class JavaScriptWithSourceMap /// public string Code { get; set; } + /// + /// The hash of the input file. + /// + public string Hash { get; set; } + /// /// The source map for this code /// diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index c65dd39ec..7d2cd5ebe 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -8,6 +8,7 @@ */ using System; +using System.Diagnostics; using System.IO; using React.Exceptions; @@ -21,11 +22,19 @@ public class JsxTransformer : IJsxTransformer /// /// Cache key for JSX to JavaScript compilation /// - private const string JSX_CACHE_KEY = "JSX_{0}"; + private const string JSX_CACHE_KEY = "JSX_v2_{0}"; /// /// Suffix to append to compiled files /// private const string COMPILED_FILE_SUFFIX = ".generated.js"; + /// + /// Suffix to append to source map files + /// + private const string SOURE_MAP_FILE_SUFFIX = ".map"; + /// + /// Number of lines in the header prepended to compiled JSX files. + /// + private const int LINES_IN_HEADER = 5; /// /// Environment this JSX Transformer has been created in @@ -73,63 +82,157 @@ public JsxTransformer(IReactEnvironment environment, ICache cache, IFileSystem f /// JavaScript public string TransformJsxFile(string filename, bool? useHarmony = null) { - var fullPath = _fileSystem.MapPath(filename); + return TransformJsxFileWithSourceMap(filename, false, useHarmony).Code; + } - // 1. Check in-memory cache - return _cache.GetOrInsert( - key: string.Format(JSX_CACHE_KEY, filename), - slidingExpiration: TimeSpan.FromMinutes(30), - cacheDependencyFiles: new[] { fullPath }, - getData: () => + /// + /// Transforms a JSX file to regular JavaScript and also returns a source map to map the + /// compiled source to the original version. Results of the JSX to JavaScript + /// transformation are cached. + /// + /// Name of the file to load + /// + /// true to re-transform the file if a cached version with no source map is available + /// + /// true if support for ES6 syntax should be enabled + /// JavaScript and source map + public JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bool forceGenerateSourceMap = false, bool? useHarmony = null) + { + var cacheKey = string.Format(JSX_CACHE_KEY, filename); + + // 1. Check in-memory cache. We need to invalidate any in-memory cache if there's no + // source map cached and forceGenerateSourceMap is true. + var cached = _cache.Get(cacheKey); + var cacheIsValid = cached != null && (!forceGenerateSourceMap || cached.SourceMap != null); + if (cacheIsValid) + { + return cached; + } + + // 2. Check on-disk cache + var contents = _fileSystem.ReadAsString(filename); + var hash = _fileCacheHash.CalculateHash(contents); + var output = LoadJsxFromFileCache(filename, hash, forceGenerateSourceMap); + if (output == null) + { + // 3. Not cached, perform the transformation + try { - // 2. Check on-disk cache - var contents = _fileSystem.ReadAsString(filename); - var hash = _fileCacheHash.CalculateHash(contents); + output = TransformJsxWithHeader(filename, contents, hash, useHarmony); + } + catch (JsxException ex) + { + // Add the filename to the error message + throw new JsxException(string.Format( + "In file \"{0}\": {1}", + filename, + ex.Message + )); + } + } - var cacheFilename = GetJsxOutputPath(filename); - if (_fileSystem.FileExists(cacheFilename)) - { - var cacheContents = _fileSystem.ReadAsString(cacheFilename); - if (_fileCacheHash.ValidateHash(cacheContents, hash)) - { - // Cache is valid :D - return cacheContents; - } - } + // Cache the result from above (either disk cache or live transformation) to memory + var fullPath = _fileSystem.MapPath(filename); + _cache.Set( + cacheKey, + output, + slidingExpiration: TimeSpan.FromMinutes(30), + cacheDependencyFiles: new[] { fullPath } + ); + return output; + } - // 3. Not cached, perform the transformation - try - { - return TransformJsxWithHeader(contents, hash, useHarmony); - } - catch (JsxException ex) + /// + /// Loads a transformed JSX file from the disk cache. If the cache is invalid or there is + /// no cached version, returns null. + /// + /// Name of the file to load + /// /// Hash of the input file, to validate the cache + /// + /// true to re-transform the file if a cached version with no source map is available + /// + /// + private JavaScriptWithSourceMap LoadJsxFromFileCache(string filename, string hash, bool forceGenerateSourceMap) + { + var cacheFilename = GetJsxOutputPath(filename); + if (!_fileSystem.FileExists(cacheFilename)) + { + // Cache file doesn't exist on disk + return null; + } + var cacheContents = _fileSystem.ReadAsString(cacheFilename); + if (!_fileCacheHash.ValidateHash(cacheContents, hash)) + { + // Hash of the cache is invalid (file changed since the time the cache was written). + return null; + } + + // Cache is valid :D + // See if we have a source map cached alongside the file + SourceMap sourceMap = null; + var sourceMapFilename = GetSourceMapOutputPath(filename); + if (_fileSystem.FileExists(sourceMapFilename)) + { + try + { + var sourceMapString = _fileSystem.ReadAsString(sourceMapFilename); + if (!string.IsNullOrEmpty(sourceMapString)) { - // Add the filename to the error message - throw new JsxException(string.Format( - "In file \"{0}\": {1}", - filename, - ex.Message - )); + sourceMap = SourceMap.FromJson(sourceMapString); } } - ); + catch (Exception e) + { + // Just ignore it + Trace.WriteLine("Error reading source map file: " + e.Message); + } + } + + // If forceGenerateSourceMap is true, we need to explicitly ignore this cached version + // if there's no source map + if (forceGenerateSourceMap && sourceMap == null) + { + return null; + } + + return new JavaScriptWithSourceMap + { + Code = cacheContents, + SourceMap = sourceMap, + Hash = hash, + }; } /// /// Transforms JSX into regular JavaScript, and prepends a header used for caching /// purposes. /// + /// Name of the file being transformed /// Contents of the input file /// Hash of the input. If null, it will be calculated /// true if support for es6 syntax should be rewritten. /// JavaScript - private string TransformJsxWithHeader(string contents, string hash = null, bool? useHarmony = null) + private JavaScriptWithSourceMap TransformJsxWithHeader(string filename, string contents, string hash = null, bool? useHarmony = null) { if (string.IsNullOrEmpty(hash)) { hash = _fileCacheHash.CalculateHash(contents); } - return GetFileHeader(hash) + TransformJsx(contents, useHarmony); + var header = GetFileHeader(hash); + //var result = TransformJsxWithSourceMap(header + contents, useHarmony); + var result = TransformJsxWithSourceMap(header + contents, useHarmony); + result.Hash = hash; + if (result.SourceMap != null) + { + // Insert original source into source map so the browser doesn't have to do a second + // request for it. The newlines in the beginning are a hack so the line numbers line + // up (as the original file doesn't have the header the transformed file includes). + result.SourceMap.Sources = new[] { Path.GetFileName(filename) + ".source" }; + result.SourceMap.SourcesContent = new[] { new string('\n', LINES_IN_HEADER) + contents }; + result.SourceMap.File = null; + } + + return result; } /// @@ -212,6 +315,17 @@ public string GetJsxOutputPath(string path) ); } + /// + /// Returns the path the specified JSX file's source map will be cached to if + /// is called. + /// + /// Path of the JSX file + /// Output path of the source map + public string GetSourceMapOutputPath(string path) + { + return GetJsxOutputPath(path) + SOURE_MAP_FILE_SUFFIX; + } + /// /// Transforms a JSX file to JavaScript, and saves the result into a ".generated.js" file /// alongside the original file. @@ -222,9 +336,11 @@ public string GetJsxOutputPath(string path) public string TransformAndSaveJsxFile(string filename, bool? useHarmony = null) { var outputPath = GetJsxOutputPath(filename); + var sourceMapPath = GetSourceMapOutputPath(filename); var contents = _fileSystem.ReadAsString(filename); - var result = TransformJsxWithHeader(contents, useHarmony: useHarmony); - _fileSystem.WriteAsString(outputPath, result); + var result = TransformJsxWithHeader(filename, contents, useHarmony: useHarmony); + _fileSystem.WriteAsString(outputPath, result.Code); + _fileSystem.WriteAsString(sourceMapPath, result.SourceMap == null ? string.Empty : result.SourceMap.ToJson()); return outputPath; } diff --git a/src/React/NullCache.cs b/src/React/NullCache.cs index 79c0cc539..c1916368a 100644 --- a/src/React/NullCache.cs +++ b/src/React/NullCache.cs @@ -18,15 +18,28 @@ namespace React public class NullCache : ICache { /// - /// Get an item from the cache. If it doesn't exist, call the function to load it + /// Get an item from the cache. Returns if the item does + /// not exist. /// /// Type of data - /// The cache key. + /// The cache key + /// Value to return if item is not in the cache + /// Data from cache, otherwise + public T Get(string key, T fallback = default(T)) + { + return fallback; + } + + /// + /// Sets an item in the cache. + /// + /// Type of data + /// The cache key + /// Data to cache /// /// Sliding expiration, if cache key is not accessed in this time period it will /// automatically be removed from the cache /// - /// Function to load data to cache. Called if data isn't in the cache, or is stale /// /// Filenames this cached item is dependent on. If any of these files change, the cache /// will be cleared automatically @@ -35,11 +48,10 @@ public class NullCache : ICache /// Other cache keys this cached item is dependent on. If any of these keys change, the /// cache will be cleared automatically /// - /// Data - public T GetOrInsert(string key, TimeSpan slidingExpiration, Func getData, IEnumerable cacheDependencyFiles = null, + public void Set(string key, T data, TimeSpan slidingExpiration, IEnumerable cacheDependencyFiles = null, IEnumerable cacheDependencyKeys = null) { - return getData(); + // no-op } } } \ No newline at end of file diff --git a/src/React/SourceMap.cs b/src/React/SourceMap.cs index b17c9da6b..069948e54 100644 --- a/src/React/SourceMap.cs +++ b/src/React/SourceMap.cs @@ -9,6 +9,8 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace React { @@ -57,5 +59,28 @@ public class SourceMap /// A string with the mapping data encoded in base 64 VLQ. /// public string Mappings { get; set; } + + /// + /// Outputs this source map as JSON. + /// + /// + public string ToJson() + { + return JsonConvert.SerializeObject(this, new JsonSerializerSettings + { + // Camelcase keys (eg. "SourcesContent" -> "sourcesContent") + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + } + + /// + /// Parse a source map from JSON + /// + /// JSON input + /// Source map + public static SourceMap FromJson(string json) + { + return JsonConvert.DeserializeObject(json); + } } } From dff1c7c876431dfd7da6051a9bfff5364cada578 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 9 Nov 2014 18:42:54 -0800 Subject: [PATCH 018/859] Link README badges to correct places --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d67eb2f81..85dc47f47 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ ReactJS.NET is a library that makes it easier to use Facebook's [React](http://facebook.github.io/react/) and [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) from C#. -[![Build status](http://img.shields.io/teamcity/codebetter/bt1242.svg)]((http://teamcity.codebetter.com/viewType.html?buildTypeId=bt1242&guest=1))  -![Code coverage](http://img.shields.io/teamcity/coverage/bt1242.svg)  -![NuGet downloads](http://img.shields.io/nuget/dt/React.Core.svg)  -![NuGet version](http://img.shields.io/nuget/v/React.Core.svg) +[![Build status](http://img.shields.io/teamcity/codebetter/bt1242.svg)](http://teamcity.codebetter.com/viewType.html?buildTypeId=bt1242&guest=1)  +[![Code coverage](http://img.shields.io/teamcity/coverage/bt1242.svg)](http://teamcity.codebetter.com/viewType.html?buildTypeId=bt1242&guest=1)  +[![NuGet downloads](http://img.shields.io/nuget/dt/React.Core.svg)](https://www.nuget.org/packages/React.Core/)  +[![NuGet version](http://img.shields.io/nuget/v/React.Core.svg)](https://www.nuget.org/packages/React.Core/) Features ======== From 067b847dead8758cd2fe9e785ffe28719ae1e1a7 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 9 Nov 2014 19:10:58 -0800 Subject: [PATCH 019/859] Use links for ClearScript DLLs rather than a local copy Always use ClearScript DLLs from the `packages` directory so `React.Sample.Mvc4` doesn't need its own copy. --- .../React.Sample.Mvc4.csproj | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj b/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj index 059fc3c60..a243fec81 100644 --- a/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj +++ b/src/React.Sample.Mvc4/React.Sample.Mvc4.csproj @@ -136,18 +136,22 @@ - + + ClearScript.V8\ClearScriptV8-32.dll PreserveNewest - - + + + ClearScript.V8\ClearScriptV8-64.dll PreserveNewest - - + + + ClearScript.V8\v8-ia32.dll PreserveNewest - - + + + ClearScript.V8\v8-x64.dll PreserveNewest - + From 303ab72cfa74175473f3430cdd3a424ca5e05f6b Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 15 Nov 2014 20:23:23 -0800 Subject: [PATCH 020/859] Add the ability to specify the HTML tag the component should be rendered to. --- src/React.Tests/Core/ReactComponentTest.cs | 19 ++++++++++ src/React.Web.Mvc4/HtmlHelperExtensions.cs | 8 ++++- src/React/IReactComponent.cs | 15 ++++++++ src/React/ReactComponent.cs | 40 +++++++++++++--------- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/React.Tests/Core/ReactComponentTest.cs b/src/React.Tests/Core/ReactComponentTest.cs index 1d4103aad..ee15cc511 100644 --- a/src/React.Tests/Core/ReactComponentTest.cs +++ b/src/React.Tests/Core/ReactComponentTest.cs @@ -63,6 +63,25 @@ public void RenderHtmlShouldWrapComponentInDiv() Assert.AreEqual(@"
[HTML]
", result); } + [Test] + public void RenderHtmlShouldWrapComponentInCustomElement() + { + var config = new Mock(); + var environment = new Mock(); + environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); + environment.Setup(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")) + .Returns("[HTML]"); + + var component = new ReactComponent(environment.Object, config.Object, "Foo", "container") + { + Props = new { hello = "World" }, + ContainerTag = "span" + }; + var result = component.RenderHtml(); + + Assert.AreEqual(@"[HTML]", result); + } + [Test] public void RenderJavaScriptShouldCallRenderComponent() { diff --git a/src/React.Web.Mvc4/HtmlHelperExtensions.cs b/src/React.Web.Mvc4/HtmlHelperExtensions.cs index 04bc2e0eb..354aa6eb4 100644 --- a/src/React.Web.Mvc4/HtmlHelperExtensions.cs +++ b/src/React.Web.Mvc4/HtmlHelperExtensions.cs @@ -35,14 +35,20 @@ private static IReactEnvironment Environment /// HTML helper /// Name of the component /// Props to initialise the component with + /// HTML tag to wrap the component in. Defaults to <div> /// The component's HTML public static IHtmlString React( this HtmlHelper htmlHelper, string componentName, - T props + T props, + string htmlTag = null ) { var reactComponent = Environment.CreateComponent(componentName, props); + if (!string.IsNullOrEmpty(htmlTag)) + { + reactComponent.ContainerTag = htmlTag; + } var result = reactComponent.RenderHtml(); return new HtmlString(result); } diff --git a/src/React/IReactComponent.cs b/src/React/IReactComponent.cs index ce2b9d862..2136981e4 100644 --- a/src/React/IReactComponent.cs +++ b/src/React/IReactComponent.cs @@ -19,6 +19,21 @@ public interface IReactComponent ///
object Props { get; set; } + /// + /// Gets or sets the name of the component + /// + string ComponentName { get; set; } + + /// + /// Gets or sets the unique ID for the container of this component + /// + string ContainerId { get; set; } + + /// + /// Gets or sets the HTML tag the component is wrapped in + /// + string ContainerTag { get; set; } + /// /// Renders the HTML for this component. This will execute the component server-side and /// return the rendered HTML. diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index a7c0ebd96..04daada77 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -38,14 +38,19 @@ public class ReactComponent : IReactComponent private readonly IReactSiteConfiguration _configuration; /// - /// Name of the component + /// Gets or sets the name of the component /// - private readonly string _componentName; + public string ComponentName { get; set; } /// - /// Unique ID for the DIV container of this component + /// Gets or sets the unique ID for the DIV container of this component /// - private readonly string _containerId; + public string ContainerId { get; set; } + + /// + /// Gets or sets the HTML tag the component is wrapped in + /// + public string ContainerTag { get; set; } /// /// Gets or sets the props for this component @@ -64,8 +69,9 @@ public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration con EnsureComponentNameValid(componentName); _environment = environment; _configuration = configuration; - _componentName = componentName; - _containerId = containerId; + ComponentName = componentName; + ContainerId = containerId; + ContainerTag = "div"; } /// @@ -81,20 +87,20 @@ public string RenderHtml() var html = _environment.Execute( string.Format("React.renderToString({0})", GetComponentInitialiser()) ); - // TODO: Allow changing of the wrapper tag element from a DIV to something else return string.Format( - "
{1}
", - _containerId, - html - ); + "<{2} id=\"{0}\">{1}", + ContainerId, + html, + ContainerTag + ); } catch (JsRuntimeException ex) { throw new ReactServerRenderingException(string.Format( "Error while rendering \"{0}\" to \"{2}\": {1}", - _componentName, + ComponentName, ex.Message, - _containerId + ContainerId )); } } @@ -110,7 +116,7 @@ public string RenderJavaScript() return string.Format( "React.render({0}, document.getElementById({1}))", GetComponentInitialiser(), - JsonConvert.SerializeObject(_containerId, _configuration.JsonSerializerSettings) // SerializeObject accepts null settings + JsonConvert.SerializeObject(ContainerId, _configuration.JsonSerializerSettings) // SerializeObject accepts null settings ); } @@ -122,14 +128,14 @@ private void EnsureComponentExists() // This is safe as componentName was validated via EnsureComponentNameValid() var componentExists = _environment.Execute(string.Format( "typeof {0} !== 'undefined'", - _componentName + ComponentName )); if (!componentExists) { throw new ReactInvalidComponentException(string.Format( "Could not find a component named '{0}'. Did you forget to add it to " + "App_Start\\ReactConfig.cs?", - _componentName + ComponentName )); } } @@ -143,7 +149,7 @@ private string GetComponentInitialiser() var encodedProps = JsonConvert.SerializeObject(Props, _configuration.JsonSerializerSettings); // SerializeObject accepts null settings return string.Format( "{0}({1})", - _componentName, + ComponentName, encodedProps ); } From 656c0ceaef3737b2c456c179dc0458dffdee1a4b Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 16 Nov 2014 00:41:40 -0800 Subject: [PATCH 021/859] Bump version number --- build.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.proj b/build.proj index cedb1acf0..e127dbf17 100644 --- a/build.proj +++ b/build.proj @@ -11,7 +11,7 @@ of patent rights can be found in the PATENTS file in the same directory. 1 1 - 3 + 4 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks From 106d66bb95d021620485faab80de5289360dc8ee Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 16 Nov 2014 14:49:04 -0800 Subject: [PATCH 022/859] Use React element factory to instantiate component. Closes #46 --- src/React.Sample.Mvc4/Content/Sample.jsx | 3 +-- src/React.Sample.Mvc4/Views/Home/Index.cshtml | 2 +- src/React.Tests/Core/ReactComponentTest.cs | 8 ++++---- src/React/IReactEnvironment.cs | 5 +++++ src/React/ReactComponent.cs | 2 +- src/React/ReactEnvironment.cs | 8 ++++++++ 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/React.Sample.Mvc4/Content/Sample.jsx b/src/React.Sample.Mvc4/Content/Sample.jsx index fa3e1c4ff..ce874fd5b 100644 --- a/src/React.Sample.Mvc4/Content/Sample.jsx +++ b/src/React.Sample.Mvc4/Content/Sample.jsx @@ -9,8 +9,7 @@ var CommentsBox = React.createClass({ propTypes: { - initialComments: React.PropTypes.array.isRequired, - commentsPerPage: React.PropTypes.number.isRequired + initialComments: React.PropTypes.array.isRequired }, getInitialState() { return { diff --git a/src/React.Sample.Mvc4/Views/Home/Index.cshtml b/src/React.Sample.Mvc4/Views/Home/Index.cshtml index 3fd670119..28f280b98 100644 --- a/src/React.Sample.Mvc4/Views/Home/Index.cshtml +++ b/src/React.Sample.Mvc4/Views/Home/Index.cshtml @@ -17,7 +17,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() diff --git a/src/React.Tests/Core/ReactComponentTest.cs b/src/React.Tests/Core/ReactComponentTest.cs index ee15cc511..1cd076a5b 100644 --- a/src/React.Tests/Core/ReactComponentTest.cs +++ b/src/React.Tests/Core/ReactComponentTest.cs @@ -42,7 +42,7 @@ public void RenderHtmlShouldCallRenderComponent() }; component.RenderHtml(); - environment.Verify(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")); + environment.Verify(x => x.Execute(@"React.renderToString(React.createElement(Foo, {""hello"":""World""}))")); } [Test] @@ -50,7 +50,7 @@ public void RenderHtmlShouldWrapComponentInDiv() { var environment = new Mock(); environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); - environment.Setup(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")) + environment.Setup(x => x.Execute(@"React.renderToString(React.createElement(Foo, {""hello"":""World""}))")) .Returns("[HTML]"); var config = new Mock(); @@ -69,7 +69,7 @@ public void RenderHtmlShouldWrapComponentInCustomElement() var config = new Mock(); var environment = new Mock(); environment.Setup(x => x.Execute("typeof Foo !== 'undefined'")).Returns(true); - environment.Setup(x => x.Execute(@"React.renderToString(Foo({""hello"":""World""}))")) + environment.Setup(x => x.Execute(@"React.renderToString(React.createElement(Foo, {""hello"":""World""}))")) .Returns("[HTML]"); var component = new ReactComponent(environment.Object, config.Object, "Foo", "container") @@ -95,7 +95,7 @@ public void RenderJavaScriptShouldCallRenderComponent() var result = component.RenderJavaScript(); Assert.AreEqual( - @"React.render(Foo({""hello"":""World""}), document.getElementById(""container""))", + @"React.render(React.createElement(Foo, {""hello"":""World""}), document.getElementById(""container""))", result ); } diff --git a/src/React/IReactEnvironment.cs b/src/React/IReactEnvironment.cs index b14f8b2ff..9077e8d08 100644 --- a/src/React/IReactEnvironment.cs +++ b/src/React/IReactEnvironment.cs @@ -27,6 +27,11 @@ public interface IReactEnvironment ///
string Version { get; } + /// + /// Gets the name and version of the JavaScript engine in use by ReactJS.NET + /// + string EngineVersion { get; } + /// /// Executes the provided JavaScript code. /// diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index 04daada77..dc162b7da 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -148,7 +148,7 @@ private string GetComponentInitialiser() { var encodedProps = JsonConvert.SerializeObject(Props, _configuration.JsonSerializerSettings); // SerializeObject accepts null settings return string.Format( - "{0}({1})", + "React.createElement({0}, {1})", ComponentName, encodedProps ); diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index 46fde07d0..faca94c71 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -131,6 +131,14 @@ public bool EngineSupportsJsxTransformer get { return Engine.SupportsJsxTransformer(); } } + /// + /// Gets the version of the JavaScript engine in use by ReactJS.NET + /// + public string EngineVersion + { + get { return Engine.Name + " " + Engine.Version; } + } + /// /// Gets the version number of ReactJS.NET /// From 02297fac5b32f6fbd669e0d98fa281f425bffe30 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 16 Nov 2014 15:11:34 -0800 Subject: [PATCH 023/859] Fix hash suffix in FileCacheHashTests --- .gitignore | 1 + src/React.Tests/Core/FileCacheHashTests.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f75550ee1..ad0084af0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ site/jekyll/_site src/React.Sample.Cassette/cassette-cache src/React.Sample.Mvc4/ClearScript.V8 *.generated.js +*.generated.js.map ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. diff --git a/src/React.Tests/Core/FileCacheHashTests.cs b/src/React.Tests/Core/FileCacheHashTests.cs index b8fd5716e..d5666bd4c 100644 --- a/src/React.Tests/Core/FileCacheHashTests.cs +++ b/src/React.Tests/Core/FileCacheHashTests.cs @@ -55,7 +55,7 @@ public void ValidateHashShouldReturnFalseWhenHashDoesNotMatch() public void ValidateHashShouldReturnTrueWhenHashMatches() { var hash = new FileCacheHash(); - Assert.IsTrue(hash.ValidateHash("// @hash " + SAMPLE_HASH + "\nHello World", SAMPLE_HASH)); + Assert.IsTrue(hash.ValidateHash("// @hash v2-" + SAMPLE_HASH + "\nHello World", SAMPLE_HASH)); } } } From d2f7332b97b52ab1eb7dd1870d9291985349aff4 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 16 Nov 2014 15:30:39 -0800 Subject: [PATCH 024/859] Switch various methods from `private` to `protected virtual` so they can be overridden. References #39 --- src/React/FileCacheHash.cs | 6 +-- src/React/JavaScriptEngineFactory.cs | 10 ++--- src/React/JsxTransformer.cs | 38 +++++++++--------- src/React/ReactComponent.cs | 12 +++--- src/React/ReactEnvironment.cs | 60 ++++++++++++++-------------- src/React/SystemEnvironmentUtils.cs | 2 +- 6 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/React/FileCacheHash.cs b/src/React/FileCacheHash.cs index 59a31c905..14710eb91 100644 --- a/src/React/FileCacheHash.cs +++ b/src/React/FileCacheHash.cs @@ -21,7 +21,7 @@ public class FileCacheHash : IFileCacheHash /// /// Prefix used for hash line in transformed file. Used for caching. /// - internal const string HASH_PREFIX = "// @hash v2-"; + private const string HASH_PREFIX = "// @hash v2-"; /// /// Althorithm for calculating file hashes @@ -47,7 +47,7 @@ public string CalculateHash(string input) /// Contents retrieved from cache /// Hash of the input /// true if the cache is still valid - public bool ValidateHash(string cacheContents, string hash) + public virtual bool ValidateHash(string cacheContents, string hash) { if (string.IsNullOrWhiteSpace(cacheContents)) { @@ -75,7 +75,7 @@ public bool ValidateHash(string cacheContents, string hash) /// /// Hash to prepend prefix to /// Hash with prefix - public string AddPrefix(string hash) + public virtual string AddPrefix(string hash) { return HASH_PREFIX + hash; } diff --git a/src/React/JavaScriptEngineFactory.cs b/src/React/JavaScriptEngineFactory.cs index ef8e555b0..f9d9ecdae 100644 --- a/src/React/JavaScriptEngineFactory.cs +++ b/src/React/JavaScriptEngineFactory.cs @@ -17,11 +17,11 @@ public class JavaScriptEngineFactory : IDisposable, IJavaScriptEngineFactory /// /// Function used to create new JavaScript engine instances. /// - private readonly Func _factory; + protected readonly Func _factory; /// /// Contains all current JavaScript engine instances. One per thread, keyed on thread ID. /// - private readonly ConcurrentDictionary _engines + protected readonly ConcurrentDictionary _engines = new ConcurrentDictionary(); /// @@ -40,7 +40,7 @@ public JavaScriptEngineFactory(IEnumerable availableFactories) /// Should handle initialisation. /// /// The JavaScript engine - public IJsEngine GetEngineForCurrentThread(Action onNewEngine = null) + public virtual IJsEngine GetEngineForCurrentThread(Action onNewEngine = null) { return _engines.GetOrAdd(Thread.CurrentThread.ManagedThreadId, id => { @@ -56,7 +56,7 @@ public IJsEngine GetEngineForCurrentThread(Action onNewEngine = null) /// /// Disposes the JavaScript engine for the current thread. /// - public void DisposeEngineForCurrentThread() + public virtual void DisposeEngineForCurrentThread() { IJsEngine engine; if (_engines.TryRemove(Thread.CurrentThread.ManagedThreadId, out engine)) @@ -112,7 +112,7 @@ private static Func GetFactory(IEnumerable availableFac /// /// Clean up all engines /// - public void Dispose() + public virtual void Dispose() { foreach (var engine in _engines) { diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index 7d2cd5ebe..97f8465d8 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -22,40 +22,40 @@ public class JsxTransformer : IJsxTransformer /// /// Cache key for JSX to JavaScript compilation /// - private const string JSX_CACHE_KEY = "JSX_v2_{0}"; + protected const string JSX_CACHE_KEY = "JSX_v2_{0}"; /// /// Suffix to append to compiled files /// - private const string COMPILED_FILE_SUFFIX = ".generated.js"; + protected const string COMPILED_FILE_SUFFIX = ".generated.js"; /// /// Suffix to append to source map files /// - private const string SOURE_MAP_FILE_SUFFIX = ".map"; + protected const string SOURE_MAP_FILE_SUFFIX = ".map"; /// /// Number of lines in the header prepended to compiled JSX files. /// - private const int LINES_IN_HEADER = 5; + protected const int LINES_IN_HEADER = 5; /// /// Environment this JSX Transformer has been created in /// - private readonly IReactEnvironment _environment; + protected readonly IReactEnvironment _environment; /// /// Cache used for storing compiled JSX /// - private readonly ICache _cache; + protected readonly ICache _cache; /// /// File system wrapper /// - private readonly IFileSystem _fileSystem; + protected readonly IFileSystem _fileSystem; /// /// Hash algorithm for file-based cache /// - private readonly IFileCacheHash _fileCacheHash; + protected readonly IFileCacheHash _fileCacheHash; /// /// Site-wide configuration /// - private readonly IReactSiteConfiguration _config; + protected readonly IReactSiteConfiguration _config; /// /// Initializes a new instance of the class. @@ -80,7 +80,7 @@ public JsxTransformer(IReactEnvironment environment, ICache cache, IFileSystem f /// Name of the file to load /// true if support for es6 syntax should be rewritten. /// JavaScript - public string TransformJsxFile(string filename, bool? useHarmony = null) + public virtual string TransformJsxFile(string filename, bool? useHarmony = null) { return TransformJsxFileWithSourceMap(filename, false, useHarmony).Code; } @@ -96,7 +96,7 @@ public string TransformJsxFile(string filename, bool? useHarmony = null) /// /// true if support for ES6 syntax should be enabled /// JavaScript and source map - public JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bool forceGenerateSourceMap = false, bool? useHarmony = null) + public virtual JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bool forceGenerateSourceMap = false, bool? useHarmony = null) { var cacheKey = string.Format(JSX_CACHE_KEY, filename); @@ -152,7 +152,7 @@ public JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bo /// true to re-transform the file if a cached version with no source map is available /// /// - private JavaScriptWithSourceMap LoadJsxFromFileCache(string filename, string hash, bool forceGenerateSourceMap) + protected virtual JavaScriptWithSourceMap LoadJsxFromFileCache(string filename, string hash, bool forceGenerateSourceMap) { var cacheFilename = GetJsxOutputPath(filename); if (!_fileSystem.FileExists(cacheFilename)) @@ -212,7 +212,7 @@ private JavaScriptWithSourceMap LoadJsxFromFileCache(string filename, string has /// Hash of the input. If null, it will be calculated /// true if support for es6 syntax should be rewritten. /// JavaScript - private JavaScriptWithSourceMap TransformJsxWithHeader(string filename, string contents, string hash = null, bool? useHarmony = null) + protected virtual JavaScriptWithSourceMap TransformJsxWithHeader(string filename, string contents, string hash = null, bool? useHarmony = null) { if (string.IsNullOrEmpty(hash)) { @@ -242,7 +242,7 @@ private JavaScriptWithSourceMap TransformJsxWithHeader(string filename, string c /// JSX /// true if support for es6 syntax should be rewritten. /// JavaScript - public string TransformJsx(string input, bool? useHarmony = null) + public virtual string TransformJsx(string input, bool? useHarmony = null) { EnsureJsxTransformerSupported(); try @@ -267,7 +267,7 @@ public string TransformJsx(string input, bool? useHarmony = null) /// JSX /// true if support for ES6 syntax should be enabled /// JavaScript and source map - public JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony) + public virtual JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony) { EnsureJsxTransformerSupported(); try @@ -290,7 +290,7 @@ public JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? use /// /// Hash of the input /// Header for the cache - private string GetFileHeader(string hash) + protected virtual string GetFileHeader(string hash) { return string.Format( @"{0} @@ -307,7 +307,7 @@ private string GetFileHeader(string hash) /// /// Path of the JSX file /// Output path of the compiled file - public string GetJsxOutputPath(string path) + public virtual string GetJsxOutputPath(string path) { return Path.Combine( Path.GetDirectoryName(path), @@ -321,7 +321,7 @@ public string GetJsxOutputPath(string path) ///
/// Path of the JSX file /// Output path of the source map - public string GetSourceMapOutputPath(string path) + public virtual string GetSourceMapOutputPath(string path) { return GetJsxOutputPath(path) + SOURE_MAP_FILE_SUFFIX; } @@ -333,7 +333,7 @@ public string GetSourceMapOutputPath(string path) /// Name of the file to load /// true if support for es6 syntax should be rewritten. /// File contents - public string TransformAndSaveJsxFile(string filename, bool? useHarmony = null) + public virtual string TransformAndSaveJsxFile(string filename, bool? useHarmony = null) { var outputPath = GetJsxOutputPath(filename); var sourceMapPath = GetSourceMapOutputPath(filename); diff --git a/src/React/ReactComponent.cs b/src/React/ReactComponent.cs index dc162b7da..dd27373b8 100644 --- a/src/React/ReactComponent.cs +++ b/src/React/ReactComponent.cs @@ -30,12 +30,12 @@ public class ReactComponent : IReactComponent /// /// Environment this component has been created in /// - private readonly IReactEnvironment _environment; + protected readonly IReactEnvironment _environment; /// /// Global site configuration /// - private readonly IReactSiteConfiguration _configuration; + protected readonly IReactSiteConfiguration _configuration; /// /// Gets or sets the name of the component @@ -79,7 +79,7 @@ public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration con /// return the rendered HTML. /// /// HTML - public string RenderHtml() + public virtual string RenderHtml() { EnsureComponentExists(); try @@ -111,7 +111,7 @@ public string RenderHtml() /// server-rendered HTML. ///
/// JavaScript - public string RenderJavaScript() + public virtual string RenderJavaScript() { return string.Format( "React.render({0}, document.getElementById({1}))", @@ -123,7 +123,7 @@ public string RenderJavaScript() /// /// Ensures that this component exists in global scope /// - private void EnsureComponentExists() + protected virtual void EnsureComponentExists() { // This is safe as componentName was validated via EnsureComponentNameValid() var componentExists = _environment.Execute(string.Format( @@ -144,7 +144,7 @@ private void EnsureComponentExists() /// Gets the JavaScript code to initialise the component ///
/// JavaScript for component initialisation - private string GetComponentInitialiser() + protected virtual string GetComponentInitialiser() { var encodedProps = JsonConvert.SerializeObject(Props, _configuration.JsonSerializerSettings); // SerializeObject accepts null settings return string.Format( diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index d0c925515..8796e7178 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -29,55 +29,55 @@ public class ReactEnvironment : IReactEnvironment, IDisposable /// /// Format string used for React component container IDs /// - private const string CONTAINER_ELEMENT_NAME = "react{0}"; + protected const string CONTAINER_ELEMENT_NAME = "react{0}"; /// /// JavaScript variable set when user-provided scripts have been loaded /// - private const string USER_SCRIPTS_LOADED_KEY = "_ReactNET_UserScripts_Loaded"; + protected const string USER_SCRIPTS_LOADED_KEY = "_ReactNET_UserScripts_Loaded"; /// /// Stack size to use for JSXTransformer if the default stack is insufficient /// - private const int LARGE_STACK_SIZE = 2 * 1024 * 1024; + protected const int LARGE_STACK_SIZE = 2 * 1024 * 1024; /// /// Factory to create JavaScript engines /// - private readonly IJavaScriptEngineFactory _engineFactory; + protected readonly IJavaScriptEngineFactory _engineFactory; /// /// Site-wide configuration /// - private readonly IReactSiteConfiguration _config; + protected readonly IReactSiteConfiguration _config; /// /// Cache used for storing compiled JSX /// - private readonly ICache _cache; + protected readonly ICache _cache; /// /// File system wrapper /// - private readonly IFileSystem _fileSystem; + protected readonly IFileSystem _fileSystem; /// /// Hash algorithm for file-based cache /// - private readonly IFileCacheHash _fileCacheHash; + protected readonly IFileCacheHash _fileCacheHash; /// /// JSX Transformer instance for this environment /// - private readonly Lazy _jsxTransformer; + protected readonly Lazy _jsxTransformer; /// /// Version number of ReactJS.NET /// - private readonly Lazy _version = new Lazy(GetVersion); + protected readonly Lazy _version = new Lazy(GetVersion); /// /// Number of components instantiated in this environment /// - private int _maxContainerId = 0; + protected int _maxContainerId = 0; /// /// List of all components instantiated in this environment /// - private readonly IList _components = new List(); + protected readonly IList _components = new List(); /// /// Initializes a new instance of the class. @@ -109,7 +109,7 @@ IFileCacheHash fileCacheHash /// Gets the JavaScript engine for the current thread. If an engine has not yet been /// created, create it and execute the startup scripts. /// - private IJsEngine Engine + protected virtual IJsEngine Engine { get { @@ -120,7 +120,7 @@ private IJsEngine Engine /// /// Gets the JSX Transformer for this environment. /// - public IJsxTransformer JsxTransformer + public virtual IJsxTransformer JsxTransformer { get { return _jsxTransformer.Value; } } @@ -128,7 +128,7 @@ public IJsxTransformer JsxTransformer /// /// Determines if this JavaScript engine supports the JSX transformer. /// - public bool EngineSupportsJsxTransformer + public virtual bool EngineSupportsJsxTransformer { get { return Engine.SupportsJsxTransformer(); } } @@ -136,7 +136,7 @@ public bool EngineSupportsJsxTransformer /// /// Gets the version of the JavaScript engine in use by ReactJS.NET /// - public string EngineVersion + public virtual string EngineVersion { get { return Engine.Name + " " + Engine.Version; } } @@ -144,7 +144,7 @@ public string EngineVersion /// /// Gets the version number of ReactJS.NET /// - public string Version + public virtual string Version { get { return _version.Value; } } @@ -152,9 +152,9 @@ public string Version /// /// Loads standard React and JSXTransformer scripts into the engine. /// - private void InitialiseEngine(IJsEngine engine) + protected virtual void InitialiseEngine(IJsEngine engine) { - var thisAssembly = GetType().Assembly; + var thisAssembly = typeof(ReactEnvironment).Assembly; engine.ExecuteResource("React.Resources.shims.js", thisAssembly); engine.ExecuteResource("React.Resources.react-with-addons.js", thisAssembly); engine.Execute("var React = global.React"); @@ -169,7 +169,7 @@ private void InitialiseEngine(IJsEngine engine) /// /// Ensures any user-provided scripts have been loaded /// - private void EnsureUserScriptsLoaded() + protected virtual void EnsureUserScriptsLoaded() { // Scripts already loaded into this environment, don't load them again if (Engine.HasVariable(USER_SCRIPTS_LOADED_KEY) || _config == null) @@ -200,7 +200,7 @@ private void EnsureUserScriptsLoaded() /// Executes the provided JavaScript code. ///
/// JavaScript to execute - public void Execute(string code) + public virtual void Execute(string code) { try { @@ -218,7 +218,7 @@ public void Execute(string code) /// Type to return /// Code to execute /// Result of the JavaScript code - public T Execute(string code) + public virtual T Execute(string code) { try { @@ -237,7 +237,7 @@ public T Execute(string code) /// JavaScript function to execute /// Arguments to pass to function /// Result of the JavaScript code - public T Execute(string function, params object[] args) + public virtual T Execute(string function, params object[] args) { try { @@ -273,7 +273,7 @@ public T Execute(string function, params object[] args) ///
/// Name of the variable /// true if the variable exists; false otherwise - public bool HasVariable(string name) + public virtual bool HasVariable(string name) { try { @@ -292,7 +292,7 @@ public bool HasVariable(string name) /// Name of the component /// Props to use /// The component - public IReactComponent CreateComponent(string componentName, T props) + public virtual IReactComponent CreateComponent(string componentName, T props) { EnsureUserScriptsLoaded(); _maxContainerId++; @@ -310,7 +310,7 @@ public IReactComponent CreateComponent(string componentName, T props) /// attach event handlers to the server-rendered HTML. ///
/// JavaScript for all components - public string GetInitJavaScript() + public virtual string GetInitJavaScript() { var fullScript = new StringBuilder(); foreach (var component in _components) @@ -355,7 +355,7 @@ public string TransformJsx(string input) /// JavaScript function to execute /// Arguments to pass to function /// Result returned from JavaScript code - public T ExecuteWithLargerStackIfRequired(string function, params object[] args) + public virtual T ExecuteWithLargerStackIfRequired(string function, params object[] args) { try { @@ -413,7 +413,7 @@ private static string GetVersion() /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// - public void Dispose() + public virtual void Dispose() { _engineFactory.DisposeEngineForCurrentThread(); } @@ -424,7 +424,7 @@ public void Dispose() /// /// Original exception /// New exception - private JsRuntimeException WrapJavaScriptRuntimeException(JsRuntimeException ex) + protected virtual JsRuntimeException WrapJavaScriptRuntimeException(JsRuntimeException ex) { return new JsRuntimeException(string.Format( "{0}\r\nLine: {1}\r\nColumn:{2}", @@ -445,7 +445,7 @@ private JsRuntimeException WrapJavaScriptRuntimeException(JsRuntimeException ex) /// /// Gets the site-wide configuration. /// - public IReactSiteConfiguration Configuration + public virtual IReactSiteConfiguration Configuration { get { return _config; } } diff --git a/src/React/SystemEnvironmentUtils.cs b/src/React/SystemEnvironmentUtils.cs index ec02fec03..be1b66ba2 100644 --- a/src/React/SystemEnvironmentUtils.cs +++ b/src/React/SystemEnvironmentUtils.cs @@ -15,7 +15,7 @@ namespace React /// /// Utility functions for handling system environmental differences /// - public class SystemEnvironmentUtils + public static class SystemEnvironmentUtils { [DllImport("libc")] private static extern int uname(IntPtr buf); From 848412245d51790a67a29e7d30f933826b2b9b37 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 16 Nov 2014 15:57:00 -0800 Subject: [PATCH 025/859] Better handling for ASP.NET IoC lifetime when calling from outside an ASP.NET request. References #39 --- .../Exceptions/ReactAspNetException.cs | 39 +++++++++++++++++++ src/React.Web/React.Web.csproj | 1 + src/React.Web/TinyIoCAspNetExtensions.cs | 14 ++++++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/React.Web/Exceptions/ReactAspNetException.cs diff --git a/src/React.Web/Exceptions/ReactAspNetException.cs b/src/React.Web/Exceptions/ReactAspNetException.cs new file mode 100644 index 000000000..8c8ee48f3 --- /dev/null +++ b/src/React.Web/Exceptions/ReactAspNetException.cs @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System; +using System.Runtime.Serialization; +using React.Exceptions; + +namespace React.Web.Exceptions +{ + [Serializable] + public class ReactAspNetException : ReactException + { + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public ReactAspNetException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public ReactAspNetException(string message, Exception innerException) + : base(message, innerException) { } + + /// + /// Used by deserialization + /// + protected ReactAspNetException(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } +} diff --git a/src/React.Web/React.Web.csproj b/src/React.Web/React.Web.csproj index ab37b3cd9..fab78efb6 100644 --- a/src/React.Web/React.Web.csproj +++ b/src/React.Web/React.Web.csproj @@ -67,6 +67,7 @@ + diff --git a/src/React.Web/TinyIoCAspNetExtensions.cs b/src/React.Web/TinyIoCAspNetExtensions.cs index 822533c70..517c54a45 100644 --- a/src/React.Web/TinyIoCAspNetExtensions.cs +++ b/src/React.Web/TinyIoCAspNetExtensions.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Web; using React.TinyIoC; +using React.Web.Exceptions; namespace React.Web.TinyIoC { @@ -35,7 +36,8 @@ public class HttpContextLifetimeProvider : TinyIoCContainer.ITinyIoCObjectLifeti /// Object instance or null public object GetObject() { - return HttpContext.Current.Items[_keyName]; + var context = HttpContext.Current; + return context == null ? null : context.Items[_keyName]; } /// @@ -44,7 +46,15 @@ public object GetObject() /// Object to store public void SetObject(object value) { - HttpContext.Current.Items[_keyName] = value; + var context = HttpContext.Current; + if (context == null) + { + throw new ReactAspNetException( + "Trying to store item in HttpContext.Current while not in an ASP.NET " + + "request!" + ); + } + context.Items[_keyName] = value; } /// From 6a681d13524b0c9e47576edd72aeb04576d6cfe2 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Mon, 17 Nov 2014 23:05:36 -0800 Subject: [PATCH 026/859] Add missing XML docblock --- src/React.Web/Exceptions/ReactAspNetException.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/React.Web/Exceptions/ReactAspNetException.cs b/src/React.Web/Exceptions/ReactAspNetException.cs index 8c8ee48f3..459b03dea 100644 --- a/src/React.Web/Exceptions/ReactAspNetException.cs +++ b/src/React.Web/Exceptions/ReactAspNetException.cs @@ -13,6 +13,9 @@ namespace React.Web.Exceptions { + /// + /// Thrown when an error occurs in ReactJS.NET's ASP.NET integration. + /// [Serializable] public class ReactAspNetException : ReactException { From 8ca07d5fa744880f8d7618cf4d2d7a4feb9cbc94 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 22 Nov 2014 20:45:49 -0800 Subject: [PATCH 027/859] Allow a custom container ID to be provided. Closes #50 --- src/React.Tests/Core/ReactEnvironmentTest.cs | 28 ++++++++++++++++++++ src/React.Web.Mvc4/HtmlHelperExtensions.cs | 6 +++-- src/React/IReactEnvironment.cs | 3 ++- src/React/ReactEnvironment.cs | 11 +++++--- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/React.Tests/Core/ReactEnvironmentTest.cs b/src/React.Tests/Core/ReactEnvironmentTest.cs index 361594199..2ac433f51 100644 --- a/src/React.Tests/Core/ReactEnvironmentTest.cs +++ b/src/React.Tests/Core/ReactEnvironmentTest.cs @@ -8,6 +8,8 @@ */ using System; +using System.Collections.Generic; +using System.Linq; using JavaScriptEngineSwitcher.Core; using Moq; using NUnit.Framework; @@ -66,6 +68,32 @@ public void ExecuteWithLargerStackIfRequiredShouldBubbleExceptions() }); } + [Test] + public void GeneratesContainerIdIfNotProvided() + { + var mocks = new Mocks(); + var environment = mocks.CreateReactEnvironment(); + mocks.Config.Setup(x => x.Scripts).Returns(new List()); + + var component1 = environment.CreateComponent("ComponentName", new { }); + var component2 = environment.CreateComponent("ComponentName", new { }); + Assert.AreEqual("react1", component1.ContainerId); + Assert.AreEqual("react2", component2.ContainerId); + } + + [Test] + public void UsesProvidedContainerId() + { + var mocks = new Mocks(); + var environment = mocks.CreateReactEnvironment(); + mocks.Config.Setup(x => x.Scripts).Returns(new List()); + + var component1 = environment.CreateComponent("ComponentName", new { }, "foo"); + var component2 = environment.CreateComponent("ComponentName", new { }); + Assert.AreEqual("foo", component1.ContainerId); + Assert.AreEqual("react1", component2.ContainerId); + } + private class Mocks { public Mock Engine { get; private set; } diff --git a/src/React.Web.Mvc4/HtmlHelperExtensions.cs b/src/React.Web.Mvc4/HtmlHelperExtensions.cs index 354aa6eb4..b1bc4a4e0 100644 --- a/src/React.Web.Mvc4/HtmlHelperExtensions.cs +++ b/src/React.Web.Mvc4/HtmlHelperExtensions.cs @@ -36,15 +36,17 @@ private static IReactEnvironment Environment /// Name of the component /// Props to initialise the component with /// HTML tag to wrap the component in. Defaults to <div> + /// ID to use for the container HTML tag. Defaults to an auto-generated ID /// The component's HTML public static IHtmlString React( this HtmlHelper htmlHelper, string componentName, T props, - string htmlTag = null + string htmlTag = null, + string containerId = null ) { - var reactComponent = Environment.CreateComponent(componentName, props); + var reactComponent = Environment.CreateComponent(componentName, props, containerId); if (!string.IsNullOrEmpty(htmlTag)) { reactComponent.ContainerTag = htmlTag; diff --git a/src/React/IReactEnvironment.cs b/src/React/IReactEnvironment.cs index 9077e8d08..c868c18a5 100644 --- a/src/React/IReactEnvironment.cs +++ b/src/React/IReactEnvironment.cs @@ -81,8 +81,9 @@ public interface IReactEnvironment /// Type of the props /// Name of the component /// Props to use + /// ID to use for the container HTML tag. Defaults to an auto-generated ID /// The component - IReactComponent CreateComponent(string componentName, T props); + IReactComponent CreateComponent(string componentName, T props, string containerId = null); /// /// Renders the JavaScript required to initialise all components client-side. This will diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index 8796e7178..b497938ee 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -291,12 +291,17 @@ public virtual bool HasVariable(string name) /// Type of the props /// Name of the component /// Props to use + /// ID to use for the container HTML tag. Defaults to an auto-generated ID /// The component - public virtual IReactComponent CreateComponent(string componentName, T props) + public virtual IReactComponent CreateComponent(string componentName, T props, string containerId = null) { EnsureUserScriptsLoaded(); - _maxContainerId++; - var containerId = string.Format(CONTAINER_ELEMENT_NAME, _maxContainerId); + if (string.IsNullOrEmpty(containerId)) + { + _maxContainerId++; + containerId = string.Format(CONTAINER_ELEMENT_NAME, _maxContainerId); + } + var component = new ReactComponent(this, _config, componentName, containerId) { Props = props From d1d82665adf007715038fd722118983357fe6c76 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 22 Nov 2014 21:19:04 -0800 Subject: [PATCH 028/859] Add `Html.ReactWithInit` helper method. Closes #42 This helper method renders both the HTML for the component, and its client-side initialisation script. It is useful when rendering self-contained partial views containing React components. --- .../Mvc/HtmlHelperExtensionsTests.cs | 58 +++++++++++++++++++ src/React.Tests/React.Tests.csproj | 33 +++++++++++ src/React.Tests/packages.config | 4 ++ src/React.Web.Mvc4/HtmlHelperExtensions.cs | 33 +++++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/React.Tests/Mvc/HtmlHelperExtensionsTests.cs diff --git a/src/React.Tests/Mvc/HtmlHelperExtensionsTests.cs b/src/React.Tests/Mvc/HtmlHelperExtensionsTests.cs new file mode 100644 index 000000000..67da31802 --- /dev/null +++ b/src/React.Tests/Mvc/HtmlHelperExtensionsTests.cs @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +using System.Runtime.Remoting; +using Moq; +using NUnit.Framework; +using React.Web.Mvc; + +namespace React.Tests.Mvc +{ + [TestFixture] + class HtmlHelperExtensionsTests + { + /// + /// Creates a mock and registers it with the IoC container + /// This is only required because can not be + /// injected :( + /// + private Mock ConfigureMockEnvironment() + { + var environment = new Mock(); + AssemblyRegistration.Container.Register(environment.Object); + return environment; + } + + [Test] + public void ReactWithInitShouldReturnHtmlAndScript() + { + var component = new Mock(); + component.Setup(x => x.RenderHtml()).Returns("HTML"); + component.Setup(x => x.RenderJavaScript()).Returns("JS"); + var environment = ConfigureMockEnvironment(); + environment.Setup(x => x.CreateComponent( + "ComponentName", + new {}, + null + )).Returns(component.Object); + + var result = HtmlHelperExtensions.ReactWithInit( + htmlHelper: null, + componentName: "ComponentName", + props: new { }, + htmlTag: "span" + ); + Assert.AreEqual( + "HTML" + System.Environment.NewLine + "", + result.ToString() + ); + + } + } +} diff --git a/src/React.Tests/React.Tests.csproj b/src/React.Tests/React.Tests.csproj index 9bfe7736c..f28f5cdef 100644 --- a/src/React.Tests/React.Tests.csproj +++ b/src/React.Tests/React.Tests.csproj @@ -44,6 +44,10 @@ False ..\packages\JavaScriptEngineSwitcher.Core.1.2.0\lib\net40\JavaScriptEngineSwitcher.Core.dll + + True + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + False ..\packages\Moq.4.2.1408.0717\lib\net40\Moq.dll @@ -54,6 +58,30 @@ + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + + + True + ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll + + + True + ..\packages\Microsoft.AspNet.Razor.2.0.20710.0\lib\net40\System.Web.Razor.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + @@ -71,6 +99,7 @@ + @@ -78,6 +107,10 @@ + + {662d52ac-1ee9-4372-bd74-379f9ac56451} + React.Web.Mvc4 + {134edd16-8dc8-4983-a2e0-b38dab1ecf1c} React.Web diff --git a/src/React.Tests/packages.config b/src/React.Tests/packages.config index 122509889..a050b8ba7 100644 --- a/src/React.Tests/packages.config +++ b/src/React.Tests/packages.config @@ -1,6 +1,10 @@  + + + + \ No newline at end of file diff --git a/src/React.Web.Mvc4/HtmlHelperExtensions.cs b/src/React.Web.Mvc4/HtmlHelperExtensions.cs index b1bc4a4e0..9e8019647 100644 --- a/src/React.Web.Mvc4/HtmlHelperExtensions.cs +++ b/src/React.Web.Mvc4/HtmlHelperExtensions.cs @@ -55,6 +55,39 @@ public static IHtmlString React( return new HtmlString(result); } + /// + /// Renders the specified React component, along with its client-side initialisation code. + /// Normally you would use the method, but + /// is useful when rendering self-contained partial views. + /// + /// Type of the props + /// HTML helper + /// Name of the component + /// Props to initialise the component with + /// HTML tag to wrap the component in. Defaults to <div> + /// ID to use for the container HTML tag. Defaults to an auto-generated ID + /// The component's HTML + public static IHtmlString ReactWithInit( + this HtmlHelper htmlHelper, + string componentName, + T props, + string htmlTag = null, + string containerId = null + ) + { + var reactComponent = Environment.CreateComponent(componentName, props, containerId); + if (!string.IsNullOrEmpty(htmlTag)) + { + reactComponent.ContainerTag = htmlTag; + } + var html = reactComponent.RenderHtml(); + var script = new TagBuilder("script") + { + InnerHtml = reactComponent.RenderJavaScript() + }; + return new HtmlString(html + System.Environment.NewLine + script.ToString()); + } + /// /// Renders the JavaScript required to initialise all components client-side. This will /// attach event handlers to the server-rendered HTML. From db608efd6eee057b8b5823037bb4127c3d034693 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 22 Nov 2014 21:52:45 -0800 Subject: [PATCH 029/859] Update to React 0.12.1 --- site/jekyll/getting-started/tutorial.md | 8 +- site/jekyll/guides/cassette.md | 2 +- site/jekyll/guides/server-side-rendering.md | 4 +- site/jekyll/index.md | 2 +- .../Views/Home/Index.cshtml | 2 +- src/React.Sample.Mvc4/Views/Home/Index.cshtml | 2 +- src/React/Resources/JSXTransformer.js | 2371 +++++++++++++---- src/React/Resources/react-with-addons.js | 10 +- 8 files changed, 1871 insertions(+), 530 deletions(-) diff --git a/site/jekyll/getting-started/tutorial.md b/site/jekyll/getting-started/tutorial.md index f4b975ffb..cbea953fb 100644 --- a/site/jekyll/getting-started/tutorial.md +++ b/site/jekyll/getting-started/tutorial.md @@ -66,7 +66,7 @@ Replace the contents of the new view file with the following:
- + @@ -235,7 +235,7 @@ Markdown is a simple way to format your text inline. For example, surrounding te First, add the third-party **Showdown** library to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. We will add it via NuGet (search for "Showdown" and install it, similar to how you installed ReactJS.NET earlier) and reference the script tag in your view: ```html{2} - + ``` @@ -848,7 +848,7 @@ Now that the bundle has been registered, we need to reference it from the view:
- + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() @@ -934,7 +934,7 @@ In the view, we will accept the list of comments as the model, and use `Html.Rea submitUrl = Url.Action("AddComment"), pollInterval = 2000, }) - + @Html.ReactInitJavaScript() diff --git a/site/jekyll/guides/cassette.md b/site/jekyll/guides/cassette.md index 48e7cf12c..914c6db9e 100644 --- a/site/jekyll/guides/cassette.md +++ b/site/jekyll/guides/cassette.md @@ -36,7 +36,7 @@ render from your view using Cassette: ... - + @Bundles.RenderScripts() ``` diff --git a/site/jekyll/guides/server-side-rendering.md b/site/jekyll/guides/server-side-rendering.md index c596f9cb3..729f9edcf 100644 --- a/site/jekyll/guides/server-side-rendering.md +++ b/site/jekyll/guides/server-side-rendering.md @@ -47,7 +47,7 @@ code. ```html - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() ``` @@ -62,7 +62,7 @@ code. - + ``` diff --git a/site/jekyll/index.md b/site/jekyll/index.md index 9f4bab50e..7aa903f83 100644 --- a/site/jekyll/index.md +++ b/site/jekyll/index.md @@ -106,7 +106,7 @@ bundles.Add(new JsxBundle("~/bundles/main").Include( }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() ``` diff --git a/src/React.Sample.Cassette/Views/Home/Index.cshtml b/src/React.Sample.Cassette/Views/Home/Index.cshtml index 6eb40ebbf..a6e110d99 100644 --- a/src/React.Sample.Cassette/Views/Home/Index.cshtml +++ b/src/React.Sample.Cassette/Views/Home/Index.cshtml @@ -20,7 +20,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Bundles.RenderScripts() @Html.ReactInitJavaScript() diff --git a/src/React.Sample.Mvc4/Views/Home/Index.cshtml b/src/React.Sample.Mvc4/Views/Home/Index.cshtml index 28f280b98..012d1f293 100644 --- a/src/React.Sample.Mvc4/Views/Home/Index.cshtml +++ b/src/React.Sample.Mvc4/Views/Home/Index.cshtml @@ -17,7 +17,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() diff --git a/src/React/Resources/JSXTransformer.js b/src/React/Resources/JSXTransformer.js index 34f87a2fb..5bc1d333b 100644 --- a/src/React/Resources/JSXTransformer.js +++ b/src/React/Resources/JSXTransformer.js @@ -1,5 +1,5 @@ /** - * JSXTransformer v0.12.0 + * JSXTransformer v0.12.1 */ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.JSXTransformer=e()}}(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 0) { + s = this.chars[i % 62] + s + i = Math.floor(i/62) + } + return s + }; + my.decode = function(a,b,c,d){ + for ( + b = c = ( + a === (/\W|_|^$/.test(a += "") || a) + ) - 1; + d = a.charCodeAt(c++); + ) + b = b * 62 + d - [, 48, 29, 87][d >> 5]; + return b + }; + + return my; +}({})); + +module.exports = Base62 +},{}],9:[function(_dereq_,module,exports){ /* Copyright (C) 2013 Ariya Hidayat Copyright (C) 2013 Thaddee Tyl @@ -1979,18 +2020,23 @@ process.chdir = function (dir) { throwError: true, generateStatement: true, peek: true, parseAssignmentExpression: true, parseBlock: true, parseClassExpression: true, parseClassDeclaration: true, parseExpression: true, +parseDeclareClass: true, parseDeclareFunction: true, +parseDeclareModule: true, parseDeclareVariable: true, parseForStatement: true, parseFunctionDeclaration: true, parseFunctionExpression: true, parseFunctionSourceElements: true, parseVariableIdentifier: true, -parseImportSpecifier: true, +parseImportSpecifier: true, parseInterface: true, parseLeftHandSideExpression: true, parseParams: true, validateParam: true, parseSpreadOrAssignmentExpression: true, -parseStatement: true, parseSourceElement: true, parseModuleBlock: true, parseConciseBody: true, +parseStatement: true, parseSourceElement: true, parseConciseBody: true, advanceXJSChild: true, isXJSIdentifierStart: true, isXJSIdentifierPart: true, scanXJSStringLiteral: true, scanXJSIdentifier: true, parseXJSAttributeValue: true, parseXJSChild: true, parseXJSElement: true, parseXJSExpressionContainer: true, parseXJSEmptyExpression: true, -parseTypeAnnotation: true, parseTypeAnnotatableIdentifier: true, -parseYieldExpression: true +parseFunctionTypeParam: true, +parsePrimaryType: true, +parseTypeAlias: true, +parseType: true, parseTypeAnnotatableIdentifier: true, parseTypeAnnotation: true, +parseYieldExpression: true, parseAwaitExpression: true */ (function (root, factory) { @@ -2069,24 +2115,32 @@ parseYieldExpression: true '<=', '<', '>', '!=', '!==']; Syntax = { + AnyTypeAnnotation: 'AnyTypeAnnotation', ArrayExpression: 'ArrayExpression', ArrayPattern: 'ArrayPattern', + ArrayTypeAnnotation: 'ArrayTypeAnnotation', ArrowFunctionExpression: 'ArrowFunctionExpression', AssignmentExpression: 'AssignmentExpression', BinaryExpression: 'BinaryExpression', BlockStatement: 'BlockStatement', + BooleanTypeAnnotation: 'BooleanTypeAnnotation', BreakStatement: 'BreakStatement', CallExpression: 'CallExpression', CatchClause: 'CatchClause', ClassBody: 'ClassBody', ClassDeclaration: 'ClassDeclaration', ClassExpression: 'ClassExpression', + ClassImplements: 'ClassImplements', ClassProperty: 'ClassProperty', ComprehensionBlock: 'ComprehensionBlock', ComprehensionExpression: 'ComprehensionExpression', ConditionalExpression: 'ConditionalExpression', ContinueStatement: 'ContinueStatement', DebuggerStatement: 'DebuggerStatement', + DeclareClass: 'DeclareClass', + DeclareFunction: 'DeclareFunction', + DeclareModule: 'DeclareModule', + DeclareVariable: 'DeclareVariable', DoWhileStatement: 'DoWhileStatement', EmptyStatement: 'EmptyStatement', ExportDeclaration: 'ExportDeclaration', @@ -2098,29 +2152,42 @@ parseYieldExpression: true ForStatement: 'ForStatement', FunctionDeclaration: 'FunctionDeclaration', FunctionExpression: 'FunctionExpression', + FunctionTypeAnnotation: 'FunctionTypeAnnotation', + FunctionTypeParam: 'FunctionTypeParam', + GenericTypeAnnotation: 'GenericTypeAnnotation', Identifier: 'Identifier', IfStatement: 'IfStatement', ImportDeclaration: 'ImportDeclaration', + ImportDefaultSpecifier: 'ImportDefaultSpecifier', + ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', ImportSpecifier: 'ImportSpecifier', + InterfaceDeclaration: 'InterfaceDeclaration', + InterfaceExtends: 'InterfaceExtends', + IntersectionTypeAnnotation: 'IntersectionTypeAnnotation', LabeledStatement: 'LabeledStatement', Literal: 'Literal', LogicalExpression: 'LogicalExpression', MemberExpression: 'MemberExpression', MethodDefinition: 'MethodDefinition', - ModuleDeclaration: 'ModuleDeclaration', + ModuleSpecifier: 'ModuleSpecifier', NewExpression: 'NewExpression', + NullableTypeAnnotation: 'NullableTypeAnnotation', + NumberTypeAnnotation: 'NumberTypeAnnotation', ObjectExpression: 'ObjectExpression', ObjectPattern: 'ObjectPattern', ObjectTypeAnnotation: 'ObjectTypeAnnotation', - OptionalParameter: 'OptionalParameter', - ParametricTypeAnnotation: 'ParametricTypeAnnotation', - ParametricallyTypedIdentifier: 'ParametricallyTypedIdentifier', + ObjectTypeCallProperty: 'ObjectTypeCallProperty', + ObjectTypeIndexer: 'ObjectTypeIndexer', + ObjectTypeProperty: 'ObjectTypeProperty', Program: 'Program', Property: 'Property', + QualifiedTypeIdentifier: 'QualifiedTypeIdentifier', ReturnStatement: 'ReturnStatement', SequenceExpression: 'SequenceExpression', SpreadElement: 'SpreadElement', SpreadProperty: 'SpreadProperty', + StringLiteralTypeAnnotation: 'StringLiteralTypeAnnotation', + StringTypeAnnotation: 'StringTypeAnnotation', SwitchCase: 'SwitchCase', SwitchStatement: 'SwitchStatement', TaggedTemplateExpression: 'TaggedTemplateExpression', @@ -2128,10 +2195,15 @@ parseYieldExpression: true TemplateLiteral: 'TemplateLiteral', ThisExpression: 'ThisExpression', ThrowStatement: 'ThrowStatement', + TupleTypeAnnotation: 'TupleTypeAnnotation', TryStatement: 'TryStatement', - TypeAnnotatedIdentifier: 'TypeAnnotatedIdentifier', + TypeAlias: 'TypeAlias', TypeAnnotation: 'TypeAnnotation', + TypeofTypeAnnotation: 'TypeofTypeAnnotation', + TypeParameterDeclaration: 'TypeParameterDeclaration', + TypeParameterInstantiation: 'TypeParameterInstantiation', UnaryExpression: 'UnaryExpression', + UnionTypeAnnotation: 'UnionTypeAnnotation', UpdateExpression: 'UpdateExpression', VariableDeclaration: 'VariableDeclaration', VariableDeclarator: 'VariableDeclarator', @@ -2149,7 +2221,8 @@ parseYieldExpression: true XJSAttribute: 'XJSAttribute', XJSSpreadAttribute: 'XJSSpreadAttribute', XJSText: 'XJSText', - YieldExpression: 'YieldExpression' + YieldExpression: 'YieldExpression', + AwaitExpression: 'AwaitExpression' }; PropertyKind = { @@ -2186,7 +2259,6 @@ parseYieldExpression: true IllegalBreak: 'Illegal break statement', IllegalDuplicateClassProperty: 'Illegal duplicate property in class definition', IllegalReturn: 'Illegal return statement', - IllegalYield: 'Illegal yield expression', IllegalSpread: 'Illegal spread element', StrictModeWith: 'Strict mode code may not include a with statement', StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', @@ -2209,23 +2281,29 @@ parseYieldExpression: true StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', StrictReservedWord: 'Use of future reserved word in strict mode', - NewlineAfterModule: 'Illegal newline after module', - NoFromAfterImport: 'Missing from after import', + MissingFromClause: 'Missing from clause', + NoAsAfterImportNamespace: 'Missing as after import *', InvalidModuleSpecifier: 'Invalid module specifier', - NestedModule: 'Module declaration can not be nested', NoUnintializedConst: 'Const must be initialized', ComprehensionRequiresBlock: 'Comprehension must have at least one block', ComprehensionError: 'Comprehension Error', EachNotAllowed: 'Each is not supported', InvalidXJSAttributeValue: 'XJS value should be either an expression or a quoted XJS text', ExpectedXJSClosingTag: 'Expected corresponding XJS closing tag for %0', - AdjacentXJSElements: 'Adjacent XJS elements must be wrapped in an enclosing tag' + AdjacentXJSElements: 'Adjacent XJS elements must be wrapped in an enclosing tag', + ConfusedAboutFunctionType: 'Unexpected token =>. It looks like ' + + 'you are trying to write a function type, but you ended up ' + + 'writing a grouped type followed by an =>, which is a syntax ' + + 'error. Remember, function type parameters are named so function ' + + 'types look like (name1: type1, name2: type2) => returnType. You ' + + 'probably wrote (type1) => returnType' }; // See also tools/generate-unicode-regex.py. Regex = { NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\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\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\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-\u0c33\u0c35-\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-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\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-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\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]'), - NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\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\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\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\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\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-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\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-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]') + NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\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\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\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\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\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-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\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-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'), + LeadingZeros: new RegExp('^0+(?!$)') }; // Ensure the condition is true, otherwise throw an error. @@ -2744,7 +2822,9 @@ parseYieldExpression: true // Other 2-character punctuators: ++ -- << >> && || - if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { + // Don't match these tokens if we're in a type, since they never can + // occur and can mess up types like Map> + if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0) && !state.inType) { index += 2; return { type: Token.Punctuator, @@ -3220,7 +3300,7 @@ parseYieldExpression: true } function scanRegExp() { - var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false; + var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false, tmp; lookahead = null; skipComment(); @@ -3296,19 +3376,43 @@ parseYieldExpression: true } } + tmp = pattern; + if (flags.indexOf('u') >= 0) { + // Replace each astral symbol and every Unicode code point + // escape sequence that represents such a symbol with a single + // ASCII symbol to avoid throwing on regular expressions that + // are only valid in combination with the `/u` flag. + tmp = tmp + .replace(/\\u\{([0-9a-fA-F]{5,6})\}/g, 'x') + .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 'x'); + } + + // First, detect invalid regular expressions. try { - value = new RegExp(pattern, flags); + value = new RegExp(tmp); } catch (e) { throwError({}, Messages.InvalidRegExp); } - peek(); + // Return a regular expression object for this pattern-flag pair, or + // `null` in case the current environment doesn't support the flags it + // uses. + try { + value = new RegExp(pattern, flags); + } catch (exception) { + value = null; + } + peek(); if (extra.tokenize) { return { type: Token.RegularExpression, value: value, + regex: { + pattern: pattern, + flags: flags + }, lineNumber: lineNumber, lineStart: lineStart, range: [start, index] @@ -3317,6 +3421,10 @@ parseYieldExpression: true return { literal: str, value: value, + regex: { + pattern: pattern, + flags: flags + }, range: [start, index] }; } @@ -3511,6 +3619,13 @@ parseYieldExpression: true return result; } + function rewind(token) { + index = token.range[0]; + lineNumber = token.lineNumber; + lineStart = token.lineStart; + lookahead = token; + } + function markerCreate() { if (!extra.loc && !extra.range) { return undefined; @@ -3738,8 +3853,8 @@ parseYieldExpression: true }, createFunctionDeclaration: function (id, params, defaults, body, rest, generator, expression, - returnType, parametricType) { - return { + isAsync, returnType, typeParameters) { + var funDecl = { type: Syntax.FunctionDeclaration, id: id, params: params, @@ -3749,13 +3864,19 @@ parseYieldExpression: true generator: generator, expression: expression, returnType: returnType, - parametricType: parametricType + typeParameters: typeParameters }; + + if (isAsync) { + funDecl.async = true; + } + + return funDecl; }, createFunctionExpression: function (id, params, defaults, body, rest, generator, expression, - returnType, parametricType) { - return { + isAsync, returnType, typeParameters) { + var funExpr = { type: Syntax.FunctionExpression, id: id, params: params, @@ -3765,8 +3886,14 @@ parseYieldExpression: true generator: generator, expression: expression, returnType: returnType, - parametricType: parametricType + typeParameters: typeParameters }; + + if (isAsync) { + funExpr.async = true; + } + + return funExpr; }, createIdentifier: function (name) { @@ -3778,25 +3905,110 @@ parseYieldExpression: true // are added later (like 'loc' and 'range'). This just helps // keep the shape of Identifier nodes consistent with everything // else. - typeAnnotation: undefined + typeAnnotation: undefined, + optional: undefined }; }, - createTypeAnnotation: function (typeIdentifier, parametricType, params, returnType, nullable) { + createTypeAnnotation: function (typeAnnotation) { return { type: Syntax.TypeAnnotation, - id: typeIdentifier, - parametricType: parametricType, + typeAnnotation: typeAnnotation + }; + }, + + createFunctionTypeAnnotation: function (params, returnType, rest, typeParameters) { + return { + type: Syntax.FunctionTypeAnnotation, params: params, returnType: returnType, - nullable: nullable + rest: rest, + typeParameters: typeParameters + }; + }, + + createFunctionTypeParam: function (name, typeAnnotation, optional) { + return { + type: Syntax.FunctionTypeParam, + name: name, + typeAnnotation: typeAnnotation, + optional: optional }; }, - createParametricTypeAnnotation: function (parametricTypes) { + createNullableTypeAnnotation: function (typeAnnotation) { return { - type: Syntax.ParametricTypeAnnotation, - params: parametricTypes + type: Syntax.NullableTypeAnnotation, + typeAnnotation: typeAnnotation + }; + }, + + createArrayTypeAnnotation: function (elementType) { + return { + type: Syntax.ArrayTypeAnnotation, + elementType: elementType + }; + }, + + createGenericTypeAnnotation: function (id, typeParameters) { + return { + type: Syntax.GenericTypeAnnotation, + id: id, + typeParameters: typeParameters + }; + }, + + createQualifiedTypeIdentifier: function (qualification, id) { + return { + type: Syntax.QualifiedTypeIdentifier, + qualification: qualification, + id: id + }; + }, + + createTypeParameterDeclaration: function (params) { + return { + type: Syntax.TypeParameterDeclaration, + params: params + }; + }, + + createTypeParameterInstantiation: function (params) { + return { + type: Syntax.TypeParameterInstantiation, + params: params + }; + }, + + createAnyTypeAnnotation: function () { + return { + type: Syntax.AnyTypeAnnotation + }; + }, + + createBooleanTypeAnnotation: function () { + return { + type: Syntax.BooleanTypeAnnotation + }; + }, + + createNumberTypeAnnotation: function () { + return { + type: Syntax.NumberTypeAnnotation + }; + }, + + createStringTypeAnnotation: function () { + return { + type: Syntax.StringTypeAnnotation + }; + }, + + createStringLiteralTypeAnnotation: function (token) { + return { + type: Syntax.StringLiteralTypeAnnotation, + value: token.value, + raw: source.slice(token.range[0], token.range[1]) }; }, @@ -3806,26 +4018,117 @@ parseYieldExpression: true }; }, - createObjectTypeAnnotation: function (properties, nullable) { + createTypeofTypeAnnotation: function (argument) { + return { + type: Syntax.TypeofTypeAnnotation, + argument: argument + }; + }, + + createTupleTypeAnnotation: function (types) { + return { + type: Syntax.TupleTypeAnnotation, + types: types + }; + }, + + createObjectTypeAnnotation: function (properties, indexers, callProperties) { return { type: Syntax.ObjectTypeAnnotation, properties: properties, - nullable: nullable + indexers: indexers, + callProperties: callProperties + }; + }, + + createObjectTypeIndexer: function (id, key, value, isStatic) { + return { + type: Syntax.ObjectTypeIndexer, + id: id, + key: key, + value: value, + "static": isStatic + }; + }, + + createObjectTypeCallProperty: function (value, isStatic) { + return { + type: Syntax.ObjectTypeCallProperty, + value: value, + "static": isStatic }; }, - createTypeAnnotatedIdentifier: function (identifier, annotation, isOptionalParam) { + createObjectTypeProperty: function (key, value, optional, isStatic) { return { - type: Syntax.TypeAnnotatedIdentifier, - id: identifier, - annotation: annotation + type: Syntax.ObjectTypeProperty, + key: key, + value: value, + optional: optional, + "static": isStatic + }; + }, + + createUnionTypeAnnotation: function (types) { + return { + type: Syntax.UnionTypeAnnotation, + types: types + }; + }, + + createIntersectionTypeAnnotation: function (types) { + return { + type: Syntax.IntersectionTypeAnnotation, + types: types + }; + }, + + createTypeAlias: function (id, typeParameters, right) { + return { + type: Syntax.TypeAlias, + id: id, + typeParameters: typeParameters, + right: right + }; + }, + + createInterface: function (id, typeParameters, body, extended) { + return { + type: Syntax.InterfaceDeclaration, + id: id, + typeParameters: typeParameters, + body: body, + "extends": extended + }; + }, + + createInterfaceExtends: function (id, typeParameters) { + return { + type: Syntax.InterfaceExtends, + id: id, + typeParameters: typeParameters + }; + }, + + createDeclareFunction: function (id) { + return { + type: Syntax.DeclareFunction, + id: id + }; + }, + + createDeclareVariable: function (id) { + return { + type: Syntax.DeclareVariable, + id: id }; }, - createOptionalParameter: function (identifier) { + createDeclareModule: function (id, body) { return { - type: Syntax.OptionalParameter, - id: identifier + type: Syntax.DeclareModule, + id: id, + body: body }; }, @@ -3923,11 +4226,15 @@ parseYieldExpression: true }, createLiteral: function (token) { - return { + var object = { type: Syntax.Literal, value: token.value, raw: source.slice(token.range[0], token.range[1]) }; + if (token.regex) { + object.regex = token.regex; + } + return object; }, createMemberExpression: function (accessor, object, property) { @@ -4122,8 +4429,8 @@ parseYieldExpression: true }; }, - createArrowFunctionExpression: function (params, defaults, body, rest, expression) { - return { + createArrowFunctionExpression: function (params, defaults, body, rest, expression, isAsync) { + var arrowExpr = { type: Syntax.ArrowFunctionExpression, id: null, params: params, @@ -4133,6 +4440,12 @@ parseYieldExpression: true generator: false, expression: expression }; + + if (isAsync) { + arrowExpr.async = true; + } + + return arrowExpr; }, createMethodDefinition: function (propertyType, kind, key, value) { @@ -4145,10 +4458,13 @@ parseYieldExpression: true }; }, - createClassProperty: function (propertyIdentifier) { + createClassProperty: function (key, typeAnnotation, computed, isStatic) { return { type: Syntax.ClassProperty, - id: propertyIdentifier + key: key, + typeAnnotation: typeAnnotation, + computed: computed, + "static": isStatic }; }, @@ -4159,24 +4475,43 @@ parseYieldExpression: true }; }, - createClassExpression: function (id, superClass, body, parametricType) { + createClassImplements: function (id, typeParameters) { + return { + type: Syntax.ClassImplements, + id: id, + typeParameters: typeParameters + }; + }, + + createClassExpression: function (id, superClass, body, typeParameters, superTypeParameters, implemented) { return { type: Syntax.ClassExpression, id: id, superClass: superClass, body: body, - parametricType: parametricType + typeParameters: typeParameters, + superTypeParameters: superTypeParameters, + "implements": implemented }; }, - createClassDeclaration: function (id, superClass, body, parametricType, superParametricType) { + createClassDeclaration: function (id, superClass, body, typeParameters, superTypeParameters, implemented) { return { type: Syntax.ClassDeclaration, id: id, superClass: superClass, body: body, - parametricType: parametricType, - superParametricType: superParametricType + typeParameters: typeParameters, + superTypeParameters: superTypeParameters, + "implements": implemented + }; + }, + + createModuleSpecifier: function (token) { + return { + type: Syntax.ModuleSpecifier, + value: token.value, + raw: source.slice(token.range[0], token.range[1]) }; }, @@ -4194,9 +4529,24 @@ parseYieldExpression: true }; }, - createExportDeclaration: function (declaration, specifiers, source) { + createImportDefaultSpecifier: function (id) { + return { + type: Syntax.ImportDefaultSpecifier, + id: id + }; + }, + + createImportNamespaceSpecifier: function (id) { + return { + type: Syntax.ImportNamespaceSpecifier, + id: id + }; + }, + + createExportDeclaration: function (isDefault, declaration, specifiers, source) { return { type: Syntax.ExportDeclaration, + 'default': !!isDefault, declaration: declaration, specifiers: specifiers, source: source @@ -4211,11 +4561,10 @@ parseYieldExpression: true }; }, - createImportDeclaration: function (specifiers, kind, source) { + createImportDeclaration: function (specifiers, source) { return { type: Syntax.ImportDeclaration, specifiers: specifiers, - kind: kind, source: source }; }, @@ -4228,12 +4577,10 @@ parseYieldExpression: true }; }, - createModuleDeclaration: function (id, source, body) { + createAwaitExpression: function (argument) { return { - type: Syntax.ModuleDeclaration, - id: id, - source: source, - body: body + type: Syntax.AwaitExpression, + argument: argument }; }, @@ -4357,13 +4704,21 @@ parseYieldExpression: true // Expect the next token to match the specified keyword. // If not, an exception will be thrown. - function expectKeyword(keyword) { + function expectKeyword(keyword, contextual) { var token = lex(); - if (token.type !== Token.Keyword || token.value !== keyword) { + if (token.type !== (contextual ? Token.Identifier : Token.Keyword) || + token.value !== keyword) { throwUnexpected(token); } } + // Expect the next token to match the specified contextual keyword. + // If not, an exception will be thrown. + + function expectContextualKeyword(keyword) { + return expectKeyword(keyword, true); + } + // Return true if the next token matches the specified punctuator. function match(value) { @@ -4372,15 +4727,15 @@ parseYieldExpression: true // Return true if the next token matches the specified keyword - function matchKeyword(keyword) { - return lookahead.type === Token.Keyword && lookahead.value === keyword; + function matchKeyword(keyword, contextual) { + var expectedType = contextual ? Token.Identifier : Token.Keyword; + return lookahead.type === expectedType && lookahead.value === keyword; } - // Return true if the next token matches the specified contextual keyword function matchContextualKeyword(keyword) { - return lookahead.type === Token.Identifier && lookahead.value === keyword; + return matchKeyword(keyword, true); } // Return true if the next token is an assignment operator @@ -4406,6 +4761,30 @@ parseYieldExpression: true op === '|='; } + // Note that 'yield' is treated as a keyword in strict mode, but a + // contextual keyword (identifier) in non-strict mode, so we need to + // use matchKeyword('yield', false) and matchKeyword('yield', true) + // (i.e. matchContextualKeyword) appropriately. + function matchYield() { + return state.yieldAllowed && matchKeyword('yield', !strict); + } + + function matchAsync() { + var backtrackToken = lookahead, matches = false; + + if (matchContextualKeyword('async')) { + lex(); // Make sure peekLineTerminator() starts after 'async'. + matches = !peekLineTerminator(); + rewind(backtrackToken); // Revert the lex(). + } + + return matches; + } + + function matchAwait() { + return state.awaitAllowed && matchContextualKeyword('await'); + } + function consumeSemicolon() { var line, oldIndex = index, oldLineNumber = lineNumber, oldLineStart = lineStart, oldLookahead = lookahead; @@ -4513,12 +4892,14 @@ parseYieldExpression: true // 11.1.5 Object Initialiser function parsePropertyFunction(options) { - var previousStrict, previousYieldAllowed, params, defaults, body, - marker = markerCreate(); + var previousStrict, previousYieldAllowed, previousAwaitAllowed, + params, defaults, body, marker = markerCreate(); previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = options.generator; + previousAwaitAllowed = state.awaitAllowed; + state.awaitAllowed = options.async; params = options.params || []; defaults = options.defaults || []; @@ -4528,6 +4909,7 @@ parseYieldExpression: true } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; + state.awaitAllowed = previousAwaitAllowed; return markerApply(marker, delegate.createFunctionExpression( null, @@ -4537,8 +4919,9 @@ parseYieldExpression: true options.rest || null, options.generator, body.type !== Syntax.BlockStatement, + options.async, options.returnType, - options.parametricType + options.typeParameters )); } @@ -4555,14 +4938,14 @@ parseYieldExpression: true throwErrorTolerant(tmp.stricted, tmp.message); } - method = parsePropertyFunction({ params: tmp.params, defaults: tmp.defaults, rest: tmp.rest, generator: options.generator, + async: options.async, returnType: tmp.returnType, - parametricType: options.parametricType + typeParameters: options.typeParameters }); strict = previousStrict; @@ -4602,46 +4985,138 @@ parseYieldExpression: true function parseObjectProperty() { var token, key, id, value, param, expr, computed, - marker = markerCreate(); + marker = markerCreate(), returnType; token = lookahead; computed = (token.value === '['); - if (token.type === Token.Identifier || computed) { - + if (token.type === Token.Identifier || computed || matchAsync()) { id = parseObjectPropertyKey(); + if (match(':')) { + lex(); + + return markerApply( + marker, + delegate.createProperty( + 'init', + id, + parseAssignmentExpression(), + false, + false, + computed + ) + ); + } + + if (match('(')) { + return markerApply( + marker, + delegate.createProperty( + 'init', + id, + parsePropertyMethodFunction({ + generator: false, + async: false + }), + true, + false, + computed + ) + ); + } + // Property Assignment: Getter and Setter. - if (token.value === 'get' && !(match(':') || match('('))) { + if (token.value === 'get') { computed = (lookahead.value === '['); key = parseObjectPropertyKey(); + expect('('); expect(')'); - return markerApply(marker, delegate.createProperty('get', key, parsePropertyFunction({ generator: false }), false, false, computed)); + if (match(':')) { + returnType = parseTypeAnnotation(); + } + + return markerApply( + marker, + delegate.createProperty( + 'get', + key, + parsePropertyFunction({ + generator: false, + async: false, + returnType: returnType + }), + false, + false, + computed + ) + ); } - if (token.value === 'set' && !(match(':') || match('('))) { + + if (token.value === 'set') { computed = (lookahead.value === '['); key = parseObjectPropertyKey(); + expect('('); token = lookahead; param = [ parseTypeAnnotatableIdentifier() ]; expect(')'); - return markerApply(marker, delegate.createProperty('set', key, parsePropertyFunction({ params: param, generator: false, name: token }), false, false, computed)); - } - if (match(':')) { - lex(); - return markerApply(marker, delegate.createProperty('init', id, parseAssignmentExpression(), false, false, computed)); + if (match(':')) { + returnType = parseTypeAnnotation(); + } + + return markerApply( + marker, + delegate.createProperty( + 'set', + key, + parsePropertyFunction({ + params: param, + generator: false, + async: false, + name: token, + returnType: returnType + }), + false, + false, + computed + ) + ); } - if (match('(')) { - return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: false }), true, false, computed)); + + if (token.value === 'async') { + computed = (lookahead.value === '['); + key = parseObjectPropertyKey(); + + return markerApply( + marker, + delegate.createProperty( + 'init', + key, + parsePropertyMethodFunction({ + generator: false, + async: true + }), + true, + false, + computed + ) + ); } + if (computed) { // Computed properties can only be used with full notation. throwUnexpected(lookahead); } - return markerApply(marker, delegate.createProperty('init', id, id, false, true, false)); + + return markerApply( + marker, + delegate.createProperty('init', id, id, false, true, false) + ); } + if (token.type === Token.EOF || token.type === Token.Punctuator) { if (!match('*')) { throwUnexpected(token); @@ -4768,6 +5243,18 @@ parseYieldExpression: true return expr; } + function matchAsyncFuncExprOrDecl() { + var token; + + if (matchAsync()) { + token = lookahead2(); + if (token.type === Token.Keyword && token.value === 'function') { + return true; + } + } + + return false; + } // 11.1 Primary Expressions @@ -5344,13 +5831,15 @@ parseYieldExpression: true } function parseArrowFunctionExpression(options, marker) { - var previousStrict, previousYieldAllowed, body; + var previousStrict, previousYieldAllowed, previousAwaitAllowed, body; expect('=>'); previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = false; + previousAwaitAllowed = state.awaitAllowed; + state.awaitAllowed = !!options.async; body = parseConciseBody(); if (strict && options.firstRestricted) { @@ -5362,30 +5851,47 @@ parseYieldExpression: true strict = previousStrict; state.yieldAllowed = previousYieldAllowed; + state.awaitAllowed = previousAwaitAllowed; return markerApply(marker, delegate.createArrowFunctionExpression( options.params, options.defaults, body, options.rest, - body.type !== Syntax.BlockStatement + body.type !== Syntax.BlockStatement, + !!options.async )); } function parseAssignmentExpression() { - var marker, expr, token, params, oldParenthesizedCount; + var marker, expr, token, params, oldParenthesizedCount, + backtrackToken = lookahead, possiblyAsync = false; - // Note that 'yield' is treated as a keyword in strict mode, but a - // contextual keyword (identifier) in non-strict mode, so we need - // to use matchKeyword and matchContextualKeyword appropriately. - if ((state.yieldAllowed && matchContextualKeyword('yield')) || (strict && matchKeyword('yield'))) { + if (matchYield()) { return parseYieldExpression(); } + if (matchAwait()) { + return parseAwaitExpression(); + } + oldParenthesizedCount = state.parenthesizedCount; marker = markerCreate(); + if (matchAsyncFuncExprOrDecl()) { + return parseFunctionExpression(); + } + + if (matchAsync()) { + // We can't be completely sure that this 'async' token is + // actually a contextual keyword modifying a function + // expression, so we might have to un-lex() it later by + // calling rewind(backtrackToken). + possiblyAsync = true; + lex(); + } + if (match('(')) { token = lookahead2(); if ((token.type === Token.Punctuator && token.value === ')') || token.value === '...') { @@ -5393,11 +5899,21 @@ parseYieldExpression: true if (!match('=>')) { throwUnexpected(lex()); } + params.async = possiblyAsync; return parseArrowFunctionExpression(params, marker); } } token = lookahead; + + // If the 'async' keyword is not followed by a '(' character or an + // identifier, then it can't be an arrow function modifier, and we + // should interpret it as a normal identifer. + if (possiblyAsync && !match('(') && token.type !== Token.Identifier) { + possiblyAsync = false; + rewind(backtrackToken); + } + expr = parseConditionalExpression(); if (match('=>') && @@ -5409,10 +5925,20 @@ parseYieldExpression: true params = reinterpretAsCoverFormalsList(expr.expressions); } if (params) { + params.async = possiblyAsync; return parseArrowFunctionExpression(params, marker); } } + // If we haven't returned by now, then the 'async' keyword was not + // a function modifier, and we should rewind and interpret it as a + // normal identifier. + if (possiblyAsync) { + possiblyAsync = false; + rewind(backtrackToken); + expr = parseConditionalExpression(); + } + if (matchAssign()) { // 11.13.1 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { @@ -5518,118 +6044,446 @@ parseYieldExpression: true // 12.2 Variable Statement - function parseObjectTypeAnnotation(nullable) { - var isMethod, marker, properties = [], property, propertyKey, - propertyTypeAnnotation; + function parseTypeParameterDeclaration() { + var marker = markerCreate(), paramTypes = []; + + expect('<'); + while (!match('>')) { + paramTypes.push(parseVariableIdentifier()); + if (!match('>')) { + expect(','); + } + } + expect('>'); + + return markerApply(marker, delegate.createTypeParameterDeclaration( + paramTypes + )); + } + + function parseTypeParameterInstantiation() { + var marker = markerCreate(), oldInType = state.inType, paramTypes = []; + + state.inType = true; + + expect('<'); + while (!match('>')) { + paramTypes.push(parseType()); + if (!match('>')) { + expect(','); + } + } + expect('>'); + + state.inType = oldInType; + + return markerApply(marker, delegate.createTypeParameterInstantiation( + paramTypes + )); + } + + function parseObjectTypeIndexer(marker, isStatic) { + var id, key, value; + + expect('['); + id = parseObjectPropertyKey(); + expect(':'); + key = parseType(); + expect(']'); + expect(':'); + value = parseType(); + + return markerApply(marker, delegate.createObjectTypeIndexer( + id, + key, + value, + isStatic + )); + } + + function parseObjectTypeMethodish(marker) { + var params = [], rest = null, returnType, typeParameters = null; + if (match('<')) { + typeParameters = parseTypeParameterDeclaration(); + } + + expect('('); + while (lookahead.type === Token.Identifier) { + params.push(parseFunctionTypeParam()); + if (!match(')')) { + expect(','); + } + } + + if (match('...')) { + lex(); + rest = parseFunctionTypeParam(); + } + expect(')'); + expect(':'); + returnType = parseType(); + + return markerApply(marker, delegate.createFunctionTypeAnnotation( + params, + returnType, + rest, + typeParameters + )); + } + + function parseObjectTypeMethod(marker, isStatic, key) { + var optional = false, value; + value = parseObjectTypeMethodish(marker); + + return markerApply(marker, delegate.createObjectTypeProperty( + key, + value, + optional, + isStatic + )); + } + + function parseObjectTypeCallProperty(marker, isStatic) { + var valueMarker = markerCreate(); + return markerApply(marker, delegate.createObjectTypeCallProperty( + parseObjectTypeMethodish(valueMarker), + isStatic + )); + } + + function parseObjectType(allowStatic) { + var callProperties = [], indexers = [], marker, optional = false, + properties = [], property, propertyKey, propertyTypeAnnotation, + token, isStatic; expect('{'); while (!match('}')) { marker = markerCreate(); - propertyKey = parseObjectPropertyKey(); - isMethod = match('('); - propertyTypeAnnotation = parseTypeAnnotation(); - properties.push(markerApply(marker, delegate.createProperty( - 'init', - propertyKey, - propertyTypeAnnotation, - isMethod, - false - ))); + if (allowStatic && matchContextualKeyword('static')) { + token = lex(); + isStatic = true; + } - if (!match('}')) { - if (match(',') || match(';')) { - lex(); + if (match('[')) { + indexers.push(parseObjectTypeIndexer(marker, isStatic)); + } else if (match('(') || match('<')) { + callProperties.push(parseObjectTypeCallProperty(marker, allowStatic)); + } else { + if (isStatic && match(':')) { + propertyKey = markerApply(marker, delegate.createIdentifier(token)); + throwErrorTolerant(token, Messages.StrictReservedWord); } else { - throwUnexpected(lookahead); + propertyKey = parseObjectPropertyKey(); + } + if (match('<') || match('(')) { + // This is a method property + properties.push(parseObjectTypeMethod(marker, isStatic, propertyKey)); + } else { + if (match('?')) { + lex(); + optional = true; + } + expect(':'); + propertyTypeAnnotation = parseType(); + properties.push(markerApply(marker, delegate.createObjectTypeProperty( + propertyKey, + propertyTypeAnnotation, + optional, + isStatic + ))); } } + + if (match(';')) { + lex(); + } else if (!match('}')) { + throwUnexpected(lookahead); + } } expect('}'); - return delegate.createObjectTypeAnnotation(properties, nullable); + return delegate.createObjectTypeAnnotation( + properties, + indexers, + callProperties + ); + } + + function parseGenericType() { + var marker = markerCreate(), returnType = null, + typeParameters = null, typeIdentifier, + typeIdentifierMarker = markerCreate; + + typeIdentifier = parseVariableIdentifier(); + + while (match('.')) { + expect('.'); + typeIdentifier = markerApply(marker, delegate.createQualifiedTypeIdentifier( + typeIdentifier, + parseVariableIdentifier() + )); + } + + if (match('<')) { + typeParameters = parseTypeParameterInstantiation(); + } + + return markerApply(marker, delegate.createGenericTypeAnnotation( + typeIdentifier, + typeParameters + )); } - function parseVoidTypeAnnotation() { + function parseVoidType() { var marker = markerCreate(); expectKeyword('void'); return markerApply(marker, delegate.createVoidTypeAnnotation()); } - function parseParametricTypeAnnotation() { - var marker = markerCreate(), typeIdentifier, paramTypes = []; + function parseTypeofType() { + var argument, marker = markerCreate(); + expectKeyword('typeof'); + argument = parsePrimaryType(); + return markerApply(marker, delegate.createTypeofTypeAnnotation( + argument + )); + } + + function parseTupleType() { + var marker = markerCreate(), types = []; + expect('['); + // We allow trailing commas + while (index < length && !match(']')) { + types.push(parseType()); + if (match(']')) { + break; + } + expect(','); + } + expect(']'); + return markerApply(marker, delegate.createTupleTypeAnnotation( + types + )); + } - expect('<'); - while (!match('>')) { - paramTypes.push(parseVariableIdentifier()); - if (!match('>')) { + function parseFunctionTypeParam() { + var marker = markerCreate(), name, optional = false, typeAnnotation; + name = parseVariableIdentifier(); + if (match('?')) { + lex(); + optional = true; + } + expect(':'); + typeAnnotation = parseType(); + return markerApply(marker, delegate.createFunctionTypeParam( + name, + typeAnnotation, + optional + )); + } + + function parseFunctionTypeParams() { + var ret = { params: [], rest: null }; + while (lookahead.type === Token.Identifier) { + ret.params.push(parseFunctionTypeParam()); + if (!match(')')) { expect(','); } } - expect('>'); - return markerApply(marker, delegate.createParametricTypeAnnotation( - paramTypes - )); + if (match('...')) { + lex(); + ret.rest = parseFunctionTypeParam(); + } + return ret; } - function parseTypeAnnotation(dontExpectColon) { + // The parsing of types roughly parallels the parsing of expressions, and + // primary types are kind of like primary expressions...they're the + // primitives with which other types are constructed. + function parsePrimaryType() { var typeIdentifier = null, params = null, returnType = null, - nullable = false, marker = markerCreate(), returnTypeMarker = null, - parametricType, annotation; + marker = markerCreate(), rest = null, tmp, + typeParameters, token, type, isGroupedType = false; - if (!dontExpectColon) { - expect(':'); + switch (lookahead.type) { + case Token.Identifier: + switch (lookahead.value) { + case 'any': + lex(); + return markerApply(marker, delegate.createAnyTypeAnnotation()); + case 'bool': // fallthrough + case 'boolean': + lex(); + return markerApply(marker, delegate.createBooleanTypeAnnotation()); + case 'number': + lex(); + return markerApply(marker, delegate.createNumberTypeAnnotation()); + case 'string': + lex(); + return markerApply(marker, delegate.createStringTypeAnnotation()); + } + return markerApply(marker, parseGenericType()); + case Token.Punctuator: + switch (lookahead.value) { + case '{': + return markerApply(marker, parseObjectType()); + case '[': + return parseTupleType(); + case '<': + typeParameters = parseTypeParameterDeclaration(); + expect('('); + tmp = parseFunctionTypeParams(); + params = tmp.params; + rest = tmp.rest; + expect(')'); + + expect('=>'); + + returnType = parseType(); + + return markerApply(marker, delegate.createFunctionTypeAnnotation( + params, + returnType, + rest, + typeParameters + )); + case '(': + lex(); + // Check to see if this is actually a grouped type + if (!match(')') && !match('...')) { + if (lookahead.type === Token.Identifier) { + token = lookahead2(); + isGroupedType = token.value !== '?' && token.value !== ':'; + } else { + isGroupedType = true; + } + } + + if (isGroupedType) { + type = parseType(); + expect(')'); + + // If we see a => next then someone was probably confused about + // function types, so we can provide a better error message + if (match('=>')) { + throwError({}, Messages.ConfusedAboutFunctionType); + } + + return type; + } + + tmp = parseFunctionTypeParams(); + params = tmp.params; + rest = tmp.rest; + + expect(')'); + + expect('=>'); + + returnType = parseType(); + + return markerApply(marker, delegate.createFunctionTypeAnnotation( + params, + returnType, + rest, + null /* typeParameters */ + )); + } + break; + case Token.Keyword: + switch (lookahead.value) { + case 'void': + return markerApply(marker, parseVoidType()); + case 'typeof': + return markerApply(marker, parseTypeofType()); + } + break; + case Token.StringLiteral: + token = lex(); + if (token.octal) { + throwError(token, Messages.StrictOctalLiteral); + } + return markerApply(marker, delegate.createStringLiteralTypeAnnotation( + token + )); } + throwUnexpected(lookahead); + } + + function parsePostfixType() { + var marker = markerCreate(), t = parsePrimaryType(); + if (match('[')) { + expect('['); + expect(']'); + return markerApply(marker, delegate.createArrayTypeAnnotation(t)); + } + return t; + } + + function parsePrefixType() { + var marker = markerCreate(); if (match('?')) { lex(); - nullable = true; + return markerApply(marker, delegate.createNullableTypeAnnotation( + parsePrefixType() + )); } + return parsePostfixType(); + } - if (match('{')) { - return markerApply(marker, parseObjectTypeAnnotation(nullable)); + + function parseIntersectionType() { + var marker = markerCreate(), type, types; + type = parsePrefixType(); + types = [type]; + while (match('&')) { + lex(); + types.push(parsePrefixType()); } - if (lookahead.type === Token.Identifier) { - typeIdentifier = parseVariableIdentifier(); - if (match('<')) { - parametricType = parseParametricTypeAnnotation(); - } - } else if (match('(')) { + return types.length === 1 ? + type : + markerApply(marker, delegate.createIntersectionTypeAnnotation( + types + )); + } + + function parseUnionType() { + var marker = markerCreate(), type, types; + type = parseIntersectionType(); + types = [type]; + while (match('|')) { lex(); - params = []; - while (lookahead.type === Token.Identifier || match('?')) { - params.push(parseTypeAnnotatableIdentifier( - true, /* requireTypeAnnotation */ - true /* canBeOptionalParam */ + types.push(parseIntersectionType()); + } + return types.length === 1 ? + type : + markerApply(marker, delegate.createUnionTypeAnnotation( + types )); - if (!match(')')) { - expect(','); - } - } - expect(')'); + } - returnTypeMarker = markerCreate(); - expect('=>'); + function parseType() { + var oldInType = state.inType, type; + state.inType = true; - returnType = parseTypeAnnotation(true); - } else { - if (!matchKeyword('void')) { - throwUnexpected(lookahead); - } else { - return markerApply(marker, parseVoidTypeAnnotation()); - } - } + type = parseUnionType(); - return markerApply(marker, delegate.createTypeAnnotation( - typeIdentifier, - parametricType, - params, - returnType, - nullable - )); + state.inType = oldInType; + return type; + } + + function parseTypeAnnotation() { + var marker = markerCreate(), type; + + expect(':'); + type = parseType(); + + return markerApply(marker, delegate.createTypeAnnotation(type)); } function parseVariableIdentifier() { @@ -5654,14 +6508,13 @@ parseYieldExpression: true } if (requireTypeAnnotation || match(':')) { - ident = markerApply(marker, delegate.createTypeAnnotatedIdentifier( - ident, - parseTypeAnnotation() - )); + ident.typeAnnotation = parseTypeAnnotation(); + ident = markerApply(marker, ident); } if (isOptionalParam) { - ident = markerApply(marker, delegate.createOptionalParameter(ident)); + ident.optional = true; + ident = markerApply(marker, ident); } return ident; @@ -5670,13 +6523,22 @@ parseYieldExpression: true function parseVariableDeclaration(kind) { var id, marker = markerCreate(), - init = null; + init = null, + typeAnnotationMarker = markerCreate(); if (match('{')) { id = parseObjectInitialiser(); reinterpretAsAssignmentBindingPattern(id); + if (match(':')) { + id.typeAnnotation = parseTypeAnnotation(); + markerApply(typeAnnotationMarker, id); + } } else if (match('[')) { id = parseArrayInitialiser(); reinterpretAsAssignmentBindingPattern(id); + if (match(':')) { + id.typeAnnotation = parseTypeAnnotation(); + markerApply(typeAnnotationMarker, id); + } } else { id = state.allowKeyword ? parseNonComputedProperty() : parseTypeAnnotatableIdentifier(); // 12.2.1 @@ -5741,41 +6603,18 @@ parseYieldExpression: true return markerApply(marker, delegate.createVariableDeclaration(declarations, kind)); } - // http://wiki.ecmascript.org/doku.php?id=harmony:modules - - function parseModuleDeclaration() { - var id, src, body, marker = markerCreate(); - - lex(); // 'module' - - if (peekLineTerminator()) { - throwError({}, Messages.NewlineAfterModule); - } - - switch (lookahead.type) { + // people.mozilla.org/~jorendorff/es6-draft.html - case Token.StringLiteral: - id = parsePrimaryExpression(); - body = parseModuleBlock(); - src = null; - break; + function parseModuleSpecifier() { + var marker = markerCreate(), + specifier; - case Token.Identifier: - id = parseVariableIdentifier(); - body = null; - if (!matchContextualKeyword('from')) { - throwUnexpected(lex()); - } - lex(); - src = parsePrimaryExpression(); - if (src.type !== Syntax.Literal) { - throwError({}, Messages.InvalidModuleSpecifier); - } - break; + if (lookahead.type !== Token.StringLiteral) { + throwError({}, Messages.InvalidModuleSpecifier); } - - consumeSemicolon(); - return markerApply(marker, delegate.createModuleDeclaration(id, src, body)); + specifier = delegate.createModuleSpecifier(lookahead); + lex(); + return markerApply(marker, specifier); } function parseExportBatchSpecifier() { @@ -5785,9 +6624,14 @@ parseYieldExpression: true } function parseExportSpecifier() { - var id, name = null, marker = markerCreate(); - - id = parseVariableIdentifier(); + var id, name = null, marker = markerCreate(), from; + if (matchKeyword('default')) { + lex(); + id = markerApply(marker, delegate.createIdentifier('default')); + // export {default} from "something"; + } else { + id = parseVariableIdentifier(); + } if (matchContextualKeyword('as')) { lex(); name = parseNonComputedProperty(); @@ -5797,104 +6641,207 @@ parseYieldExpression: true } function parseExportDeclaration() { - var previousAllowKeyword, decl, def, src, specifiers, + var backtrackToken, id, previousAllowKeyword, declaration = null, + isExportFromIdentifier, + src = null, specifiers = [], marker = markerCreate(); expectKeyword('export'); + if (matchKeyword('default')) { + // covers: + // export default ... + lex(); + if (matchKeyword('function') || matchKeyword('class')) { + backtrackToken = lookahead; + lex(); + if (isIdentifierName(lookahead)) { + // covers: + // export default function foo () {} + // export default class foo {} + id = parseNonComputedProperty(); + rewind(backtrackToken); + return markerApply(marker, delegate.createExportDeclaration(true, parseSourceElement(), [id], null)); + } + // covers: + // export default function () {} + // export default class {} + rewind(backtrackToken); + switch (lookahead.value) { + case 'class': + return markerApply(marker, delegate.createExportDeclaration(true, parseClassExpression(), [], null)); + case 'function': + return markerApply(marker, delegate.createExportDeclaration(true, parseFunctionExpression(), [], null)); + } + } + + if (matchContextualKeyword('from')) { + throwError({}, Messages.UnexpectedToken, lookahead.value); + } + + // covers: + // export default {}; + // export default []; + if (match('{')) { + declaration = parseObjectInitialiser(); + } else if (match('[')) { + declaration = parseArrayInitialiser(); + } else { + declaration = parseAssignmentExpression(); + } + consumeSemicolon(); + return markerApply(marker, delegate.createExportDeclaration(true, declaration, [], null)); + } + + // non-default export if (lookahead.type === Token.Keyword) { + // covers: + // export var f = 1; switch (lookahead.value) { case 'let': case 'const': case 'var': case 'class': case 'function': - return markerApply(marker, delegate.createExportDeclaration(parseSourceElement(), null, null)); + return markerApply(marker, delegate.createExportDeclaration(false, parseSourceElement(), specifiers, null)); } } - if (isIdentifierName(lookahead)) { - previousAllowKeyword = state.allowKeyword; - state.allowKeyword = true; - decl = parseVariableDeclarationList('let'); - state.allowKeyword = previousAllowKeyword; - return markerApply(marker, delegate.createExportDeclaration(decl, null, null)); + if (match('*')) { + // covers: + // export * from "foo"; + specifiers.push(parseExportBatchSpecifier()); + + if (!matchContextualKeyword('from')) { + throwError({}, lookahead.value ? + Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); + } + lex(); + src = parseModuleSpecifier(); + consumeSemicolon(); + + return markerApply(marker, delegate.createExportDeclaration(false, null, specifiers, src)); } - specifiers = []; - src = null; + expect('{'); + do { + isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default'); + specifiers.push(parseExportSpecifier()); + } while (match(',') && lex()); + expect('}'); - if (match('*')) { - specifiers.push(parseExportBatchSpecifier()); + if (matchContextualKeyword('from')) { + // covering: + // export {default} from "foo"; + // export {foo} from "foo"; + lex(); + src = parseModuleSpecifier(); + consumeSemicolon(); + } else if (isExportFromIdentifier) { + // covering: + // export {default}; // missing fromClause + throwError({}, lookahead.value ? + Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } else { - expect('{'); - do { - specifiers.push(parseExportSpecifier()); - } while (match(',') && lex()); - expect('}'); + // cover + // export {foo}; + consumeSemicolon(); } + return markerApply(marker, delegate.createExportDeclaration(false, declaration, specifiers, src)); + } - if (matchContextualKeyword('from')) { + + function parseImportSpecifier() { + // import {} ...; + var id, name = null, marker = markerCreate(); + + id = parseNonComputedProperty(); + if (matchContextualKeyword('as')) { lex(); - src = parsePrimaryExpression(); - if (src.type !== Syntax.Literal) { - throwError({}, Messages.InvalidModuleSpecifier); - } + name = parseVariableIdentifier(); } - consumeSemicolon(); + return markerApply(marker, delegate.createImportSpecifier(id, name)); + } - return markerApply(marker, delegate.createExportDeclaration(null, specifiers, src)); + function parseNamedImports() { + var specifiers = []; + // {foo, bar as bas} + expect('{'); + do { + specifiers.push(parseImportSpecifier()); + } while (match(',') && lex()); + expect('}'); + return specifiers; + } + + function parseImportDefaultSpecifier() { + // import ...; + var id, marker = markerCreate(); + + id = parseNonComputedProperty(); + + return markerApply(marker, delegate.createImportDefaultSpecifier(id)); + } + + function parseImportNamespaceSpecifier() { + // import <* as foo> ...; + var id, marker = markerCreate(); + + expect('*'); + if (!matchContextualKeyword('as')) { + throwError({}, Messages.NoAsAfterImportNamespace); + } + lex(); + id = parseNonComputedProperty(); + + return markerApply(marker, delegate.createImportNamespaceSpecifier(id)); } function parseImportDeclaration() { - var specifiers, kind, src, marker = markerCreate(); + var specifiers, src, marker = markerCreate(); expectKeyword('import'); specifiers = []; - if (isIdentifierName(lookahead)) { - kind = 'default'; - specifiers.push(parseImportSpecifier()); + if (lookahead.type === Token.StringLiteral) { + // covers: + // import "foo"; + src = parseModuleSpecifier(); + consumeSemicolon(); + return markerApply(marker, delegate.createImportDeclaration(specifiers, src)); + } - if (!matchContextualKeyword('from')) { - throwError({}, Messages.NoFromAfterImport); + if (!matchKeyword('default') && isIdentifierName(lookahead)) { + // covers: + // import foo + // import foo, ... + specifiers.push(parseImportDefaultSpecifier()); + if (match(',')) { + lex(); } - lex(); + } + if (match('*')) { + // covers: + // import foo, * as foo + // import * as foo + specifiers.push(parseImportNamespaceSpecifier()); } else if (match('{')) { - kind = 'named'; - lex(); - do { - specifiers.push(parseImportSpecifier()); - } while (match(',') && lex()); - expect('}'); - - if (!matchContextualKeyword('from')) { - throwError({}, Messages.NoFromAfterImport); - } - lex(); + // covers: + // import foo, {bar} + // import {bar} + specifiers = specifiers.concat(parseNamedImports()); } - src = parsePrimaryExpression(); - if (src.type !== Syntax.Literal) { - throwError({}, Messages.InvalidModuleSpecifier); + if (!matchContextualKeyword('from')) { + throwError({}, lookahead.value ? + Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); } - + lex(); + src = parseModuleSpecifier(); consumeSemicolon(); - return markerApply(marker, delegate.createImportDeclaration(specifiers, kind, src)); - } - - function parseImportSpecifier() { - var id, name = null, marker = markerCreate(); - - id = parseNonComputedProperty(); - if (matchContextualKeyword('as')) { - lex(); - name = parseVariableIdentifier(); - } - - return markerApply(marker, delegate.createImportSpecifier(id, name)); + return markerApply(marker, delegate.createImportDeclaration(specifiers, src)); } // 12.3 Empty Statement @@ -6447,6 +7394,10 @@ parseYieldExpression: true } } + if (matchAsyncFuncExprOrDecl()) { + return parseFunctionDeclaration(); + } + marker = markerCreate(); expr = parseExpression(); @@ -6572,7 +7523,7 @@ parseYieldExpression: true } function parseParam(options) { - var token, rest, param, def; + var marker, token, rest, param, def; token = lookahead; if (token.value === '...') { @@ -6581,19 +7532,31 @@ parseYieldExpression: true } if (match('[')) { + marker = markerCreate(); param = parseArrayInitialiser(); reinterpretAsDestructuredParameter(options, param); + if (match(':')) { + param.typeAnnotation = parseTypeAnnotation(); + markerApply(marker, param); + } } else if (match('{')) { + marker = markerCreate(); if (rest) { throwError({}, Messages.ObjectPatternAsRestParameter); } param = parseObjectInitialiser(); reinterpretAsDestructuredParameter(options, param); + if (match(':')) { + param.typeAnnotation = parseTypeAnnotation(); + markerApply(marker, param); + } } else { - // Typing rest params is awkward, so punting on that for now param = rest - ? parseVariableIdentifier() + ? parseTypeAnnotatableIdentifier( + false, /* requireTypeAnnotation */ + false /* canBeOptionalParam */ + ) : parseTypeAnnotatableIdentifier( false, /* requireTypeAnnotation */ true /* canBeOptionalParam */ @@ -6661,8 +7624,15 @@ parseYieldExpression: true } function parseFunctionDeclaration() { - var id, body, token, tmp, firstRestricted, message, previousStrict, previousYieldAllowed, generator, - marker = markerCreate(), parametricType; + var id, body, token, tmp, firstRestricted, message, generator, isAsync, + previousStrict, previousYieldAllowed, previousAwaitAllowed, + marker = markerCreate(), typeParameters; + + isAsync = false; + if (matchAsync()) { + lex(); + isAsync = true; + } expectKeyword('function'); @@ -6677,7 +7647,7 @@ parseYieldExpression: true id = parseVariableIdentifier(); if (match('<')) { - parametricType = parseParametricTypeAnnotation(); + typeParameters = parseTypeParameterDeclaration(); } if (strict) { @@ -6703,6 +7673,8 @@ parseYieldExpression: true previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = generator; + previousAwaitAllowed = state.awaitAllowed; + state.awaitAllowed = isAsync; body = parseFunctionSourceElements(); @@ -6714,14 +7686,35 @@ parseYieldExpression: true } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; + state.awaitAllowed = previousAwaitAllowed; - return markerApply(marker, delegate.createFunctionDeclaration(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false, - tmp.returnType, parametricType)); + return markerApply( + marker, + delegate.createFunctionDeclaration( + id, + tmp.params, + tmp.defaults, + body, + tmp.rest, + generator, + false, + isAsync, + tmp.returnType, + typeParameters + ) + ); } function parseFunctionExpression() { - var token, id = null, firstRestricted, message, tmp, body, previousStrict, previousYieldAllowed, generator, - marker = markerCreate(), parametricType; + var token, id = null, firstRestricted, message, tmp, body, generator, isAsync, + previousStrict, previousYieldAllowed, previousAwaitAllowed, + marker = markerCreate(), typeParameters; + + isAsync = false; + if (matchAsync()) { + lex(); + isAsync = true; + } expectKeyword('function'); @@ -6753,7 +7746,7 @@ parseYieldExpression: true } if (match('<')) { - parametricType = parseParametricTypeAnnotation(); + typeParameters = parseTypeParameterDeclaration(); } } @@ -6766,6 +7759,8 @@ parseYieldExpression: true previousStrict = strict; previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = generator; + previousAwaitAllowed = state.awaitAllowed; + state.awaitAllowed = isAsync; body = parseFunctionSourceElements(); @@ -6777,20 +7772,29 @@ parseYieldExpression: true } strict = previousStrict; state.yieldAllowed = previousYieldAllowed; + state.awaitAllowed = previousAwaitAllowed; - return markerApply(marker, delegate.createFunctionExpression(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false, - tmp.returnType, parametricType)); + return markerApply( + marker, + delegate.createFunctionExpression( + id, + tmp.params, + tmp.defaults, + body, + tmp.rest, + generator, + false, + isAsync, + tmp.returnType, + typeParameters + ) + ); } function parseYieldExpression() { - var yieldToken, delegateFlag, expr, marker = markerCreate(); - - yieldToken = lex(); - assert(yieldToken.value === 'yield', 'Called parseYieldExpression with non-yield lookahead.'); + var delegateFlag, expr, marker = markerCreate(); - if (!state.yieldAllowed) { - throwErrorTolerant({}, Messages.IllegalYield); - } + expectKeyword('yield', !strict); delegateFlag = false; if (match('*')) { @@ -6803,35 +7807,34 @@ parseYieldExpression: true return markerApply(marker, delegate.createYieldExpression(expr, delegateFlag)); } + function parseAwaitExpression() { + var expr, marker = markerCreate(); + expectContextualKeyword('await'); + expr = parseAssignmentExpression(); + return markerApply(marker, delegate.createAwaitExpression(expr)); + } + // 14 Classes - function parseMethodDefinition(existingPropNames) { - var token, key, param, propType, isValidDuplicateProp = false, - marker = markerCreate(), token2, parametricType, - parametricTypeMarker, annotationMarker; + function parseMethodDefinition(existingPropNames, key, isStatic, generator, computed) { + var token, param, propType, isValidDuplicateProp = false, + isAsync, typeParameters, tokenValue, returnType, + annotationMarker; - if (lookahead.value === 'static') { - propType = ClassPropertyType["static"]; - lex(); - } else { - propType = ClassPropertyType.prototype; - } + propType = isStatic ? ClassPropertyType["static"] : ClassPropertyType.prototype; - if (match('*')) { - lex(); - return markerApply(marker, delegate.createMethodDefinition( + if (generator) { + return delegate.createMethodDefinition( propType, '', - parseObjectPropertyKey(), + key, parsePropertyMethodFunction({ generator: true }) - )); + ); } - token = lookahead; - //parametricTypeMarker = markerCreate(); - key = parseObjectPropertyKey(); + tokenValue = key.type === 'Identifier' && key.name; - if (token.value === 'get' && !match('(')) { + if (tokenValue === 'get' && !match('(')) { key = parseObjectPropertyKey(); // It is a syntax error if any other properties have a name @@ -6854,14 +7857,17 @@ parseYieldExpression: true expect('('); expect(')'); - return markerApply(marker, delegate.createMethodDefinition( + if (match(':')) { + returnType = parseTypeAnnotation(); + } + return delegate.createMethodDefinition( propType, 'get', key, - parsePropertyFunction({ generator: false }) - )); + parsePropertyFunction({ generator: false, returnType: returnType }) + ); } - if (token.value === 'set' && !match('(')) { + if (tokenValue === 'set' && !match('(')) { key = parseObjectPropertyKey(); // It is a syntax error if any other properties have a name @@ -6886,16 +7892,29 @@ parseYieldExpression: true token = lookahead; param = [ parseTypeAnnotatableIdentifier() ]; expect(')'); - return markerApply(marker, delegate.createMethodDefinition( + if (match(':')) { + returnType = parseTypeAnnotation(); + } + return delegate.createMethodDefinition( propType, 'set', key, - parsePropertyFunction({ params: param, generator: false, name: token }) - )); + parsePropertyFunction({ + params: param, + generator: false, + name: token, + returnType: returnType + }) + ); } if (match('<')) { - parametricType = parseParametricTypeAnnotation(); + typeParameters = parseTypeParameterDeclaration(); + } + + isAsync = tokenValue === 'async' && !match('('); + if (isAsync) { + key = parseObjectPropertyKey(); } // It is a syntax error if any other properties have the same name as a @@ -6907,42 +7926,64 @@ parseYieldExpression: true } existingPropNames[propType][key.name].data = true; - return markerApply(marker, delegate.createMethodDefinition( + return delegate.createMethodDefinition( propType, '', key, parsePropertyMethodFunction({ generator: false, - parametricType: parametricType + async: isAsync, + typeParameters: typeParameters }) - )); + ); } - function parseClassProperty(existingPropNames) { - var marker = markerCreate(), propertyIdentifier; + function parseClassProperty(existingPropNames, key, computed, isStatic) { + var typeAnnotation; - propertyIdentifier = parseTypeAnnotatableIdentifier(); + typeAnnotation = parseTypeAnnotation(); expect(';'); - return markerApply(marker, delegate.createClassProperty( - propertyIdentifier - )); + return delegate.createClassProperty( + key, + typeAnnotation, + computed, + isStatic + ); } function parseClassElement(existingProps) { + var computed, generator = false, key, marker = markerCreate(), + isStatic = false; if (match(';')) { lex(); return; } - var doubleLookahead = lookahead2(); - if (doubleLookahead.type === Token.Punctuator) { - if (doubleLookahead.value === ':') { - return parseClassProperty(existingProps); - } + if (lookahead.value === 'static') { + lex(); + isStatic = true; } - return parseMethodDefinition(existingProps); + if (match('*')) { + lex(); + generator = true; + } + + computed = (lookahead.value === '['); + key = parseObjectPropertyKey(); + + if (!generator && lookahead.value === ':') { + return markerApply(marker, parseClassProperty(existingProps, key, computed, isStatic)); + } + + return markerApply(marker, parseMethodDefinition( + existingProps, + key, + isStatic, + generator, + computed + )); } function parseClassBody() { @@ -6969,66 +8010,109 @@ parseYieldExpression: true return markerApply(marker, delegate.createClassBody(classElements)); } + function parseClassImplements() { + var id, implemented = [], marker, typeParameters; + expectContextualKeyword('implements'); + while (index < length) { + marker = markerCreate(); + id = parseVariableIdentifier(); + if (match('<')) { + typeParameters = parseTypeParameterInstantiation(); + } else { + typeParameters = null; + } + implemented.push(markerApply(marker, delegate.createClassImplements( + id, + typeParameters + ))); + if (!match(',')) { + break; + } + expect(','); + } + return implemented; + } + function parseClassExpression() { - var id, previousYieldAllowed, superClass = null, marker = markerCreate(), - parametricType; + var id, implemented, previousYieldAllowed, superClass = null, + superTypeParameters, marker = markerCreate(), typeParameters; expectKeyword('class'); - if (!matchKeyword('extends') && !match('{')) { + if (!matchKeyword('extends') && !matchContextualKeyword('implements') && !match('{')) { id = parseVariableIdentifier(); } if (match('<')) { - parametricType = parseParametricTypeAnnotation(); + typeParameters = parseTypeParameterDeclaration(); } if (matchKeyword('extends')) { expectKeyword('extends'); previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = false; - superClass = parseAssignmentExpression(); + superClass = parseLeftHandSideExpressionAllowCall(); + if (match('<')) { + superTypeParameters = parseTypeParameterInstantiation(); + } state.yieldAllowed = previousYieldAllowed; } - return markerApply(marker, delegate.createClassExpression(id, superClass, parseClassBody(), parametricType)); + if (matchContextualKeyword('implements')) { + implemented = parseClassImplements(); + } + + return markerApply(marker, delegate.createClassExpression( + id, + superClass, + parseClassBody(), + typeParameters, + superTypeParameters, + implemented + )); } function parseClassDeclaration() { - var id, previousYieldAllowed, superClass = null, marker = markerCreate(), - parametricType, superParametricType; + var id, implemented, previousYieldAllowed, superClass = null, + superTypeParameters, marker = markerCreate(), typeParameters; expectKeyword('class'); id = parseVariableIdentifier(); if (match('<')) { - parametricType = parseParametricTypeAnnotation(); + typeParameters = parseTypeParameterDeclaration(); } if (matchKeyword('extends')) { expectKeyword('extends'); previousYieldAllowed = state.yieldAllowed; state.yieldAllowed = false; - superClass = parseAssignmentExpression(); + superClass = parseLeftHandSideExpressionAllowCall(); + if (match('<')) { + superTypeParameters = parseTypeParameterInstantiation(); + } state.yieldAllowed = previousYieldAllowed; } - return markerApply(marker, delegate.createClassDeclaration(id, superClass, parseClassBody(), parametricType, superParametricType)); + if (matchContextualKeyword('implements')) { + implemented = parseClassImplements(); + } + + return markerApply(marker, delegate.createClassDeclaration( + id, + superClass, + parseClassBody(), + typeParameters, + superTypeParameters, + implemented + )); } // 15 Program - function matchModuleDeclaration() { - var id; - if (matchContextualKeyword('module')) { - id = lookahead2(); - return id.type === Token.StringLiteral || id.type === Token.Identifier; - } - return false; - } - function parseSourceElement() { + var token; if (lookahead.type === Token.Keyword) { switch (lookahead.value) { case 'const': @@ -7036,17 +8120,36 @@ parseYieldExpression: true return parseConstLetDeclaration(lookahead.value); case 'function': return parseFunctionDeclaration(); - case 'export': - return parseExportDeclaration(); - case 'import': - return parseImportDeclaration(); default: return parseStatement(); } } - if (matchModuleDeclaration()) { - throwError({}, Messages.NestedModule); + if (matchContextualKeyword('type') + && lookahead2().type === Token.Identifier) { + return parseTypeAlias(); + } + + if (matchContextualKeyword('interface') + && lookahead2().type === Token.Identifier) { + return parseInterface(); + } + + if (matchContextualKeyword('declare')) { + token = lookahead2(); + if (token.type === Token.Keyword) { + switch (token.value) { + case 'class': + return parseDeclareClass(); + case 'function': + return parseDeclareFunction(); + case 'var': + return parseDeclareVariable(); + } + } else if (token.type === Token.Identifier + && token.value === 'module') { + return parseDeclareModule(); + } } if (lookahead.type !== Token.EOF) { @@ -7064,10 +8167,6 @@ parseYieldExpression: true } } - if (matchModuleDeclaration()) { - return parseModuleDeclaration(); - } - return parseSourceElement(); } @@ -7109,40 +8208,6 @@ parseYieldExpression: true return sourceElements; } - function parseModuleElement() { - return parseSourceElement(); - } - - function parseModuleElements() { - var list = [], - statement; - - while (index < length) { - if (match('}')) { - break; - } - statement = parseModuleElement(); - if (typeof statement === 'undefined') { - break; - } - list.push(statement); - } - - return list; - } - - function parseModuleBlock() { - var block, marker = markerCreate(); - - expect('{'); - - block = parseModuleElements(); - - expect('}'); - - return markerApply(marker, delegate.createBlockStatement(block)); - } - function parseProgram() { var body, marker = markerCreate(); strict = false; @@ -7613,7 +8678,7 @@ parseYieldExpression: true } function scanXJSEntity() { - var ch, str = '', count = 0, entity; + var ch, str = '', start = index, count = 0, code; ch = source.charAt(index); assert(ch === '&', 'Entity must start with an ampersand'); index++; @@ -7625,14 +8690,28 @@ parseYieldExpression: true str += ch; } - if (str[0] === '#' && str[1] === 'x') { - entity = String.fromCharCode(parseInt(str.substr(2), 16)); - } else if (str[0] === '#') { - entity = String.fromCharCode(parseInt(str.substr(1), 10)); - } else { - entity = XHTMLEntities[str]; + // Well-formed entity (ending was found). + if (ch === ';') { + // Numeric entity. + if (str[0] === '#') { + if (str[1] === 'x') { + code = +('0' + str.substr(1)); + } else { + // Removing leading zeros in order to avoid treating as octal in old browsers. + code = +str.substr(1).replace(Regex.LeadingZeros, ''); + } + + if (!isNaN(code)) { + return String.fromCharCode(code); + } + } else if (XHTMLEntities[str]) { + return XHTMLEntities[str]; + } } - return entity; + + // Treat non-entity sequences as regular text. + index = start + 1; + return '&'; } function scanXJSText(stopChars) { @@ -7959,8 +9038,181 @@ parseYieldExpression: true return markerApply(marker, delegate.createXJSElement(openingElement, closingElement, children)); } + function parseTypeAlias() { + var id, marker = markerCreate(), typeParameters = null, right; + expectContextualKeyword('type'); + id = parseVariableIdentifier(); + if (match('<')) { + typeParameters = parseTypeParameterDeclaration(); + } + expect('='); + right = parseType(); + consumeSemicolon(); + return markerApply(marker, delegate.createTypeAlias(id, typeParameters, right)); + } + + function parseInterfaceExtends() { + var marker = markerCreate(), id, typeParameters = null; + + id = parseVariableIdentifier(); + if (match('<')) { + typeParameters = parseTypeParameterInstantiation(); + } + + return markerApply(marker, delegate.createInterfaceExtends( + id, + typeParameters + )); + } + + function parseInterfaceish(marker, allowStatic) { + var body, bodyMarker, extended = [], id, + typeParameters = null; + + id = parseVariableIdentifier(); + if (match('<')) { + typeParameters = parseTypeParameterDeclaration(); + } + + if (matchKeyword('extends')) { + expectKeyword('extends'); + + while (index < length) { + extended.push(parseInterfaceExtends()); + if (!match(',')) { + break; + } + expect(','); + } + } + + bodyMarker = markerCreate(); + body = markerApply(bodyMarker, parseObjectType(allowStatic)); + + return markerApply(marker, delegate.createInterface( + id, + typeParameters, + body, + extended + )); + } + + function parseInterface() { + var body, bodyMarker, extended = [], id, marker = markerCreate(), + typeParameters = null; + + expectContextualKeyword('interface'); + return parseInterfaceish(marker, /* allowStatic */false); + } + + function parseDeclareClass() { + var marker = markerCreate(), ret; + expectContextualKeyword('declare'); + expectKeyword('class'); + + ret = parseInterfaceish(marker, /* allowStatic */true); + ret.type = Syntax.DeclareClass; + return ret; + } + + function parseDeclareFunction() { + var id, idMarker, + marker = markerCreate(), params, returnType, rest, tmp, + typeParameters = null, value, valueMarker; + + expectContextualKeyword('declare'); + expectKeyword('function'); + idMarker = markerCreate(); + id = parseVariableIdentifier(); + + valueMarker = markerCreate(); + if (match('<')) { + typeParameters = parseTypeParameterDeclaration(); + } + expect('('); + tmp = parseFunctionTypeParams(); + params = tmp.params; + rest = tmp.rest; + expect(')'); + + expect(':'); + returnType = parseType(); + + value = markerApply(valueMarker, delegate.createFunctionTypeAnnotation( + params, + returnType, + rest, + typeParameters + )); + + id.typeAnnotation = markerApply(valueMarker, delegate.createTypeAnnotation( + value + )); + markerApply(idMarker, id); + + consumeSemicolon(); + + return markerApply(marker, delegate.createDeclareFunction( + id + )); + } + + function parseDeclareVariable() { + var id, marker = markerCreate(); + expectContextualKeyword('declare'); + expectKeyword('var'); + id = parseTypeAnnotatableIdentifier(); + + consumeSemicolon(); + + return markerApply(marker, delegate.createDeclareVariable( + id + )); + } + + function parseDeclareModule() { + var body = [], bodyMarker, id, idMarker, marker = markerCreate(), token; + expectContextualKeyword('declare'); + expectContextualKeyword('module'); + + if (lookahead.type === Token.StringLiteral) { + if (strict && lookahead.octal) { + throwErrorTolerant(lookahead, Messages.StrictOctalLiteral); + } + idMarker = markerCreate(); + id = markerApply(idMarker, delegate.createLiteral(lex())); + } else { + id = parseVariableIdentifier(); + } + + bodyMarker = markerCreate(); + expect('{'); + while (index < length && !match('}')) { + token = lookahead2(); + switch (token.value) { + case 'class': + body.push(parseDeclareClass()); + break; + case 'function': + body.push(parseDeclareFunction()); + break; + case 'var': + body.push(parseDeclareVariable()); + break; + default: + throwUnexpected(lookahead); + } + } + expect('}'); + + return markerApply(marker, delegate.createDeclareModule( + id, + markerApply(bodyMarker, delegate.createBlockStatement(body)) + )); + } + function collectToken() { - var start, loc, token, range, value; + var start, loc, token, range, value, entry; if (!state.inXJSChild) { skipComment(); @@ -7983,12 +9235,19 @@ parseYieldExpression: true if (token.type !== Token.EOF) { range = [token.range[0], token.range[1]]; value = source.slice(token.range[0], token.range[1]); - extra.tokens.push({ + entry = { type: TokenName[token.type], value: value, range: range, loc: loc - }); + }; + if (token.regex) { + entry.regex = { + pattern: token.regex.pattern, + flags: token.regex.flags + }; + } + extra.tokens.push(entry); } return token; @@ -8027,6 +9286,7 @@ parseYieldExpression: true extra.tokens.push({ type: 'RegularExpression', value: regex.literal, + regex: regex.regex, range: [pos, index], loc: loc }); @@ -8044,6 +9304,12 @@ parseYieldExpression: true type: entry.type, value: entry.value }; + if (entry.regex) { + token.regex = { + pattern: entry.regex.pattern, + flags: entry.regex.flags + }; + } if (extra.range) { token.range = entry.range; } @@ -8230,8 +9496,10 @@ parseYieldExpression: true inSwitch: false, inXJSChild: false, inXJSTag: false, + inType: false, lastCommentStart: -1, - yieldAllowed: false + yieldAllowed: false, + awaitAllowed: false }; extra = {}; @@ -8302,7 +9570,7 @@ parseYieldExpression: true } // Sync with *.json manifests. - exports.version = '6001.0001.0000-dev-harmony-fb'; + exports.version = '8001.1001.0-dev-harmony-fb'; exports.tokenize = tokenize; @@ -8332,34 +9600,6 @@ parseYieldExpression: true })); /* vim: set sw=4 ts=4 et tw=80 : */ -},{}],9:[function(_dereq_,module,exports){ -var Base62 = (function (my) { - my.chars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] - - my.encode = function(i){ - if (i === 0) {return '0'} - var s = '' - while (i > 0) { - s = this.chars[i % 62] + s - i = Math.floor(i/62) - } - return s - }; - my.decode = function(a,b,c,d){ - for ( - b = c = ( - a === (/\W|_|^$/.test(a += "") || a) - ) - 1; - d = a.charCodeAt(c++); - ) - b = b * 62 + d - [, 48, 29, 87][d >> 5]; - return b - }; - - return my; -}({})); - -module.exports = Base62 },{}],10:[function(_dereq_,module,exports){ /* * Copyright 2009-2011 Mozilla Foundation and contributors @@ -10632,7 +11872,6 @@ function _nodeIsBlockScopeBoundary(node, parentNode) { /** * @param {object} node - * @param {function} visitor * @param {array} path * @param {object} state */ @@ -10830,7 +12069,7 @@ function transform(visitors, source, options) { exports.transform = transform; exports.Syntax = Syntax; -},{"./utils":22,"esprima-fb":8,"source-map":10}],22:[function(_dereq_,module,exports){ +},{"./utils":22,"esprima-fb":9,"source-map":10}],22:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -11227,7 +12466,7 @@ function indentBefore(start, state) { var end = start; start = start - 1; - while (start > 0 && state.g.source.chatAt(start) != '\n') { + while (start > 0 && state.g.source.charAt(start) != '\n') { if (!state.g.source.charAt(start).match(/[ \t]/)) { end = start; } @@ -11281,8 +12520,8 @@ function initScopeMetadata(boundaryNode, path, node) { function declareIdentInLocalScope(identName, metaData, state) { state.localScope.identifiers[identName] = { boundaryNode: metaData.boundaryNode, - path: metaData.path, - node: metaData.node, + path: metaData.bindingPath, + node: metaData.bindingNode, state: Object.create(state) }; } @@ -11299,7 +12538,6 @@ function getLexicalBindingMetadata(identName, state) { * @param {function} analyzer * @param {function} traverser * @param {object} node - * @param {function} visitor * @param {array} path * @param {object} state */ @@ -11376,16 +12614,22 @@ function enqueueNodeWithStartIndex(queue, node) { * @param {string} type - node type to lookup. */ function containsChildOfType(node, type) { + return containsChildMatching(node, function(node) { + return node.type === type; + }); +} + +function containsChildMatching(node, matcher) { var foundMatchingChild = false; function nodeTypeAnalyzer(node) { - if (node.type === type) { + if (matcher(node) === true) { foundMatchingChild = true; return false; } } function nodeTypeTraverser(child, path, state) { if (!foundMatchingChild) { - foundMatchingChild = containsChildOfType(child, type); + foundMatchingChild = containsChildMatching(child, matcher); } } analyzeAndTraverse( @@ -11414,11 +12658,20 @@ function getBoundaryNode(path) { ); } +function getTempVar(tempVarIndex) { + return '$__' + tempVarIndex; +} + +function getTempVarWithValue(tempVarIndex, tempVarValue) { + return getTempVar(tempVarIndex) + '=' + tempVarValue; +} + exports.append = append; exports.catchup = catchup; exports.catchupWhiteOut = catchupWhiteOut; exports.catchupWhiteSpace = catchupWhiteSpace; exports.catchupNewlines = catchupNewlines; +exports.containsChildMatching = containsChildMatching; exports.containsChildOfType = containsChildOfType; exports.createState = createState; exports.declareIdentInLocalScope = declareIdentInLocalScope; @@ -11436,8 +12689,10 @@ exports.updateState = updateState; exports.analyzeAndTraverse = analyzeAndTraverse; exports.getOrderedChildren = getOrderedChildren; exports.getNodeSourceText = getNodeSourceText; +exports.getTempVar = getTempVar; +exports.getTempVarWithValue = getTempVarWithValue; -},{"./docblock":20,"esprima-fb":8}],23:[function(_dereq_,module,exports){ +},{"./docblock":20,"esprima-fb":9}],23:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -11512,7 +12767,14 @@ function visitArrowFunction(traverse, node, path, state) { // Bind the function only if `this` value is used // inside it or inside any sub-expression. - if (utils.containsChildOfType(node.body, Syntax.ThisExpression)) { + var containsBindingSyntax = + utils.containsChildMatching(node.body, function(node) { + return node.type === Syntax.ThisExpression + || (node.type === Syntax.Identifier + && node.name === "super"); + }); + + if (containsBindingSyntax) { utils.append('.bind(this)', state); } @@ -11553,7 +12815,7 @@ function renderExpressionBody(traverse, node, path, state) { // Special handling of rest param. if (node.rest) { utils.append( - restParamVisitors.renderRestParamSetup(node), + restParamVisitors.renderRestParamSetup(node, state), state ); } @@ -11590,7 +12852,7 @@ exports.visitorList = [ ]; -},{"../src/utils":22,"./es6-destructuring-visitors":25,"./es6-rest-param-visitors":28,"esprima-fb":8}],24:[function(_dereq_,module,exports){ +},{"../src/utils":22,"./es6-destructuring-visitors":25,"./es6-rest-param-visitors":28,"esprima-fb":9}],24:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -11821,7 +13083,7 @@ function visitClassFunctionExpression(traverse, node, path, state) { var params = node.params; if (params.length > 0) { - utils.move(params[0].range[0], state); + utils.catchupNewlines(params[0].range[0], state); for (var i = 0; i < params.length; i++) { utils.catchup(node.params[i].range[0], state); path.unshift(node); @@ -12159,7 +13421,7 @@ exports.visitorList = [ visitSuperMemberExpression ]; -},{"../src/utils":22,"./reserved-words-helper":32,"base62":9,"esprima-fb":8}],25:[function(_dereq_,module,exports){ +},{"../src/utils":22,"./reserved-words-helper":32,"base62":8,"esprima-fb":9}],25:[function(_dereq_,module,exports){ /** * Copyright 2014 Facebook, Inc. * @@ -12215,7 +13477,7 @@ var restPropertyHelpers = _dereq_('./es7-rest-property-helpers'); function visitStructuredVariable(traverse, node, path, state) { // Allocate new temp for the pattern. - utils.append(getTmpVar(state.localScope.tempVarIndex) + '=', state); + utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state); // Skip the pattern and assign the init to the temp. utils.catchupWhiteSpace(node.init.range[0], state); traverse(node.init, path, state); @@ -12255,14 +13517,14 @@ function getDestructuredComponents(node, state) { // and also for function param patterns: [x, ...xs, y] components.push(item.argument.name + '=Array.prototype.slice.call(' + - getTmpVar(tmpIndex) + ',' + idx + ')' + utils.getTempVar(tmpIndex) + ',' + idx + ')' ); continue; } if (item.type === Syntax.SpreadProperty) { var restExpression = restPropertyHelpers.renderRestExpression( - getTmpVar(tmpIndex), + utils.getTempVar(tmpIndex), patternItems ); components.push(item.argument.name + '=' + restExpression); @@ -12281,8 +13543,8 @@ function getDestructuredComponents(node, state) { } else { // Complex sub-structure. components.push( - getInitialValue(++state.localScope.tempVarIndex, accessor) + ',' + - getDestructuredComponents(value, state) + utils.getTempVarWithValue(++state.localScope.tempVarIndex, accessor) + + ',' + getDestructuredComponents(value, state) ); } } @@ -12295,14 +13557,16 @@ function getPatternItems(node) { } function getPatternItemAccessor(node, patternItem, tmpIndex, idx) { - var tmpName = getTmpVar(tmpIndex); + var tmpName = utils.getTempVar(tmpIndex); if (node.type === Syntax.ObjectPattern) { if (reservedWordsHelper.isReservedWord(patternItem.key.name)) { return tmpName + '["' + patternItem.key.name + '"]'; - } else { + } else if (patternItem.key.type === Syntax.Literal) { + return tmpName + '[' + JSON.stringify(patternItem.key.value) + ']'; + } else if (patternItem.key.type === Syntax.Identifier) { return tmpName + '.' + patternItem.key.name; } - } else { + } else if (node.type === Syntax.ArrayPattern) { return tmpName + '[' + idx + ']'; } } @@ -12313,14 +13577,6 @@ function getPatternItemValue(node, patternItem) { : patternItem; } -function getInitialValue(index, value) { - return getTmpVar(index) + '=' + value; -} - -function getTmpVar(index) { - return '$__' + index; -} - // ------------------------------------------------------- // 2. Assignment expression. // @@ -12330,14 +13586,14 @@ function getTmpVar(index) { function visitStructuredAssignment(traverse, node, path, state) { var exprNode = node.expression; - utils.append('var ' + getTmpVar(state.localScope.tempVarIndex) + '=', state); + utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state); utils.catchupWhiteSpace(exprNode.right.range[0], state); traverse(exprNode.right, path, state); utils.catchup(exprNode.right.range[1], state); utils.append( - ',' + getDestructuredComponents(exprNode.left, state) + ';', + ';' + getDestructuredComponents(exprNode.left, state) + ';', state ); @@ -12362,7 +13618,7 @@ visitStructuredAssignment.test = function(node, path, state) { // ------------------------------------------------------- function visitStructuredParameter(traverse, node, path, state) { - utils.append(getTmpVar(getParamIndex(node, path)), state); + utils.append(utils.getTempVar(getParamIndex(node, path)), state); utils.catchupWhiteSpace(node.range[1], state); return true; } @@ -12407,7 +13663,7 @@ function visitFunctionBodyForStructuredParameter(traverse, node, path, state) { if (funcNode.rest) { utils.append( - restParamVisitors.renderRestParamSetup(funcNode), + restParamVisitors.renderRestParamSetup(funcNode, state), state ); } @@ -12447,7 +13703,7 @@ exports.visitorList = [ exports.renderDestructuredComponents = renderDestructuredComponents; -},{"../src/utils":22,"./es6-rest-param-visitors":28,"./es7-rest-property-helpers":30,"./reserved-words-helper":32,"esprima-fb":8}],26:[function(_dereq_,module,exports){ +},{"../src/utils":22,"./es6-rest-param-visitors":28,"./es7-rest-property-helpers":30,"./reserved-words-helper":32,"esprima-fb":9}],26:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -12518,7 +13774,7 @@ exports.visitorList = [ visitObjectConciseMethod ]; -},{"../src/utils":22,"./reserved-words-helper":32,"esprima-fb":8}],27:[function(_dereq_,module,exports){ +},{"../src/utils":22,"./reserved-words-helper":32,"esprima-fb":9}],27:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -12545,7 +13801,7 @@ exports.visitorList = [ * return {x, y}; // {x: x, y: y} * }; * - * // Destrucruting. + * // Destructuring. * function init({port, ip, coords: {x, y}}) { ... } * */ @@ -12573,7 +13829,7 @@ exports.visitorList = [ ]; -},{"../src/utils":22,"esprima-fb":8}],28:[function(_dereq_,module,exports){ +},{"../src/utils":22,"esprima-fb":9}],28:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -12593,16 +13849,20 @@ exports.visitorList = [ /*jslint node:true*/ /** - * Desugars ES6 rest parameters into ES3 arguments slicing. + * Desugars ES6 rest parameters into an ES3 arguments array. * * function printf(template, ...args) { * args.forEach(...); - * }; + * } + * + * We could use `Array.prototype.slice.call`, but that usage of arguments causes + * functions to be deoptimized in V8, so instead we use a for-loop. * * function printf(template) { - * var args = [].slice.call(arguments, 1); + * for (var args = [], $__0 = 1, $__1 = arguments.length; $__0 < $__1; $__0++) + * args.push(arguments[$__0]); * args.forEach(...); - * }; + * } * */ var Syntax = _dereq_('esprima-fb').Syntax; @@ -12647,17 +13907,22 @@ visitFunctionParamsWithRestParam.test = function(node, path, state) { return _nodeIsFunctionWithRestParam(node); }; -function renderRestParamSetup(functionNode) { - return 'var ' + functionNode.rest.name + '=Array.prototype.slice.call(' + - 'arguments,' + - functionNode.params.length + - ');'; +function renderRestParamSetup(functionNode, state) { + var idx = state.localScope.tempVarIndex++; + var len = state.localScope.tempVarIndex++; + + return 'for (var ' + functionNode.rest.name + '=[],' + + utils.getTempVarWithValue(idx, functionNode.params.length) + ',' + + utils.getTempVarWithValue(len, 'arguments.length') + ';' + + utils.getTempVar(idx) + '<' + utils.getTempVar(len) + ';' + + utils.getTempVar(idx) + '++) ' + + functionNode.rest.name + '.push(arguments[' + utils.getTempVar(idx) + ']);'; } function visitFunctionBodyWithRestParam(traverse, node, path, state) { utils.catchup(node.range[0] + 1, state); var parentNode = path[0]; - utils.append(renderRestParamSetup(parentNode), state); + utils.append(renderRestParamSetup(parentNode, state), state); return true; } @@ -12672,7 +13937,7 @@ exports.visitorList = [ visitFunctionBodyWithRestParam ]; -},{"../src/utils":22,"esprima-fb":8}],29:[function(_dereq_,module,exports){ +},{"../src/utils":22,"esprima-fb":9}],29:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -12830,7 +14095,7 @@ exports.visitorList = [ visitTaggedTemplateExpression ]; -},{"../src/utils":22,"esprima-fb":8}],30:[function(_dereq_,module,exports){ +},{"../src/utils":22,"esprima-fb":9}],30:[function(_dereq_,module,exports){ /** * Copyright 2013 Facebook, Inc. * @@ -12913,7 +14178,7 @@ function renderRestExpression(accessorExpression, excludedProperties) { exports.renderRestExpression = renderRestExpression; -},{"../src/utils":22,"esprima-fb":8}],31:[function(_dereq_,module,exports){ +},{"../src/utils":22,"esprima-fb":9}],31:[function(_dereq_,module,exports){ /** * Copyright 2004-present Facebook. All Rights Reserved. */ @@ -12985,7 +14250,11 @@ function visitObjectLiteralSpread(traverse, node, path, state) { } } - utils.catchup(node.range[1] - 1, state); + // Strip any non-whitespace between the last item and the end. + // We only catch up on whitespace so that we ignore any trailing commas which + // are stripped out for IE8 support. Unfortunately, this also strips out any + // trailing comments. + utils.catchupWhiteSpace(node.range[1] - 1, state); // Skip the trailing } utils.move(node.range[1], state); @@ -13019,7 +14288,7 @@ exports.visitorList = [ visitObjectLiteralSpread ]; -},{"../src/utils":22,"esprima-fb":8}],32:[function(_dereq_,module,exports){ +},{"../src/utils":22,"esprima-fb":9}],32:[function(_dereq_,module,exports){ /** * Copyright 2014 Facebook, Inc. * @@ -13061,7 +14330,7 @@ var RESERVED_WORDS = [].concat( LITERALS ); -var reservedWordsMap = {}; +var reservedWordsMap = Object.create(null); RESERVED_WORDS.forEach(function(k) { reservedWordsMap[k] = true; }); @@ -13072,7 +14341,7 @@ exports.isReservedWord = function(word) { },{}],33:[function(_dereq_,module,exports){ var esprima = _dereq_('esprima-fb'); -var utils = _dereq_('jstransform/src/utils'); +var utils = _dereq_('../src/utils'); var Syntax = esprima.Syntax; @@ -13083,6 +14352,7 @@ function _isFunctionNode(node) { } function visitClassProperty(traverse, node, path, state) { + utils.catchup(node.range[0], state); utils.catchupWhiteOut(node.range[1], state); return false; } @@ -13090,18 +14360,50 @@ visitClassProperty.test = function(node, path, state) { return node.type === Syntax.ClassProperty; }; +function visitTypeAlias(traverse, node, path, state) { + utils.catchupWhiteOut(node.range[1], state); + return false; +} +visitTypeAlias.test = function(node, path, state) { + return node.type === Syntax.TypeAlias; +}; + +function visitInterfaceDeclaration(traverse, node, path, state) { + utils.catchupWhiteOut(node.range[1], state); + return false; +} +visitInterfaceDeclaration.test = function(node, path, state) { + return node.type === Syntax.InterfaceDeclaration; +}; + +function visitDeclare(traverse, node, path, state) { + utils.catchupWhiteOut(node.range[1], state); + return false; +} +visitDeclare.test = function(node, path, state) { + switch (node.type) { + case Syntax.DeclareVariable: + case Syntax.DeclareFunction: + case Syntax.DeclareClass: + case Syntax.DeclareModule: return true + } + return false; +} + function visitFunctionParametricAnnotation(traverse, node, path, state) { + utils.catchup(node.range[0], state); utils.catchupWhiteOut(node.range[1], state); return false; } visitFunctionParametricAnnotation.test = function(node, path, state) { - return node.type === Syntax.ParametricTypeAnnotation + return node.type === Syntax.TypeParameterDeclaration && path[0] && _isFunctionNode(path[0]) - && node === path[0].parametricType; + && node === path[0].typeParameters; }; function visitFunctionReturnAnnotation(traverse, node, path, state) { + utils.catchup(node.range[0], state); utils.catchupWhiteOut(node.range[1], state); return false; } @@ -13110,38 +14412,83 @@ visitFunctionReturnAnnotation.test = function(node, path, state) { }; function visitOptionalFunctionParameterAnnotation(traverse, node, path, state) { - path.unshift(node); - traverse(node.id, path, state); - path.shift(); - utils.catchup(node.id.range[1], state); + utils.catchup(node.range[0] + node.name.length, state); utils.catchupWhiteOut(node.range[1], state); return false; } visitOptionalFunctionParameterAnnotation.test = function(node, path, state) { - return node.type === Syntax.OptionalParameter + return node.type === Syntax.Identifier + && node.optional && path[0] && _isFunctionNode(path[0]); }; function visitTypeAnnotatedIdentifier(traverse, node, path, state) { - traverse(node.id, path, state); - utils.catchup(node.id.range[1], state); - utils.catchupWhiteOut(node.range[1], state); + utils.catchup(node.typeAnnotation.range[0], state); + utils.catchupWhiteOut(node.typeAnnotation.range[1], state); return false; } visitTypeAnnotatedIdentifier.test = function(node, path, state) { - return node.type === Syntax.TypeAnnotatedIdentifier; + return node.type === Syntax.Identifier && node.typeAnnotation; +}; + +function visitTypeAnnotatedObjectOrArrayPattern(traverse, node, path, state) { + utils.catchup(node.typeAnnotation.range[0], state); + utils.catchupWhiteOut(node.typeAnnotation.range[1], state); + return false; +} +visitTypeAnnotatedObjectOrArrayPattern.test = function(node, path, state) { + var rightType = node.type === Syntax.ObjectPattern + || node.type === Syntax.ArrayPattern; + return rightType && node.typeAnnotation; +}; + +/** + * Methods cause trouble, since esprima parses them as a key/value pair, where + * the location of the value starts at the method body. For example + * { bar(x:number,...y:Array):number {} } + * is parsed as + * { bar: function(x: number, ...y:Array): number {} } + * except that the location of the FunctionExpression value is 40-something, + * which is the location of the function body. This means that by the time we + * visit the params, rest param, and return type organically, we've already + * catchup()'d passed them. + */ +function visitMethod(traverse, node, path, state) { + path.unshift(node); + traverse(node.key, path, state); + + path.unshift(node.value); + traverse(node.value.params, path, state); + node.value.rest && traverse(node.value.rest, path, state); + node.value.returnType && traverse(node.value.returnType, path, state); + traverse(node.value.body, path, state); + + path.shift(); + + path.shift(); + return false; +} + +visitMethod.test = function(node, path, state) { + return (node.type === "Property" && (node.method || node.kind === "set" || node.kind === "get")) + || (node.type === "MethodDefinition"); }; exports.visitorList = [ visitClassProperty, + visitDeclare, + visitInterfaceDeclaration, visitFunctionParametricAnnotation, visitFunctionReturnAnnotation, + visitMethod, visitOptionalFunctionParameterAnnotation, - visitTypeAnnotatedIdentifier + visitTypeAlias, + visitTypeAnnotatedIdentifier, + visitTypeAnnotatedObjectOrArrayPattern ]; -},{"esprima-fb":8,"jstransform/src/utils":22}],34:[function(_dereq_,module,exports){ +},{"../src/utils":22,"esprima-fb":9}],34:[function(_dereq_,module,exports){ /** * Copyright 2013-2014, Facebook, Inc. * All rights reserved. @@ -13153,7 +14500,7 @@ exports.visitorList = [ /*global exports:true*/ "use strict"; -var Syntax = _dereq_('esprima-fb').Syntax; +var Syntax = _dereq_('jstransform').Syntax; var utils = _dereq_('jstransform/src/utils'); var FALLBACK_TAGS = _dereq_('./xjs').knownTags; @@ -13396,7 +14743,7 @@ exports.visitorList = [ visitReactTag ]; -},{"./xjs":36,"esprima-fb":8,"jstransform/src/utils":22}],35:[function(_dereq_,module,exports){ +},{"./xjs":36,"jstransform":21,"jstransform/src/utils":22}],35:[function(_dereq_,module,exports){ /** * Copyright 2013-2014, Facebook, Inc. * All rights reserved. @@ -13408,7 +14755,7 @@ exports.visitorList = [ /*global exports:true*/ "use strict"; -var Syntax = _dereq_('esprima-fb').Syntax; +var Syntax = _dereq_('jstransform').Syntax; var utils = _dereq_('jstransform/src/utils'); function addDisplayName(displayName, object, state) { @@ -13491,7 +14838,7 @@ exports.visitorList = [ visitReactDisplayName ]; -},{"esprima-fb":8,"jstransform/src/utils":22}],36:[function(_dereq_,module,exports){ +},{"jstransform":21,"jstransform/src/utils":22}],36:[function(_dereq_,module,exports){ /** * Copyright 2013-2014, Facebook, Inc. * All rights reserved. @@ -13502,7 +14849,7 @@ exports.visitorList = [ */ /*global exports:true*/ "use strict"; -var Syntax = _dereq_('esprima-fb').Syntax; +var Syntax = _dereq_('jstransform').Syntax; var utils = _dereq_('jstransform/src/utils'); var knownTags = { @@ -13740,7 +15087,7 @@ exports.renderXJSLiteral = renderXJSLiteral; exports.quoteAttrName = quoteAttrName; exports.trimLeft = trimLeft; -},{"esprima-fb":8,"jstransform/src/utils":22}],37:[function(_dereq_,module,exports){ +},{"jstransform":21,"jstransform/src/utils":22}],37:[function(_dereq_,module,exports){ /*global exports:true*/ var es6ArrowFunctions = _dereq_('jstransform/visitors/es6-arrow-function-visitors'); var es6Classes = _dereq_('jstransform/visitors/es6-class-visitors'); @@ -13752,7 +15099,6 @@ var es6Templates = _dereq_('jstransform/visitors/es6-template-visitors'); var es7SpreadProperty = _dereq_('jstransform/visitors/es7-spread-property-visitors'); var react = _dereq_('./transforms/react'); var reactDisplayName = _dereq_('./transforms/reactDisplayName'); -var typesSyntax = _dereq_('jstransform/visitors/type-syntax'); /** * Map from transformName => orderedListOfVisitors. @@ -13766,8 +15112,7 @@ var transformVisitors = { 'es6-rest-params': es6RestParameters.visitorList, 'es6-templates': es6Templates.visitorList, 'es7-spread-property': es7SpreadProperty.visitorList, - 'react': react.visitorList.concat(reactDisplayName.visitorList), - 'types': typesSyntax.visitorList + 'react': react.visitorList.concat(reactDisplayName.visitorList) }; var transformSets = { @@ -13783,9 +15128,6 @@ var transformSets = { ], 'react': [ 'react' - ], - 'type-annotations': [ - 'types' ] }; @@ -13793,7 +15135,6 @@ var transformSets = { * Specifies the order in which each transform should run. */ var transformRunOrder = [ - 'types', 'es6-arrow-functions', 'es6-object-concise-method', 'es6-object-short-notation', @@ -13854,5 +15195,5 @@ exports.getVisitorsBySet = getVisitorsBySet; exports.getAllVisitors = getAllVisitors; exports.transformVisitors = transformVisitors; -},{"./transforms/react":34,"./transforms/reactDisplayName":35,"jstransform/visitors/es6-arrow-function-visitors":23,"jstransform/visitors/es6-class-visitors":24,"jstransform/visitors/es6-destructuring-visitors":25,"jstransform/visitors/es6-object-concise-method-visitors":26,"jstransform/visitors/es6-object-short-notation-visitors":27,"jstransform/visitors/es6-rest-param-visitors":28,"jstransform/visitors/es6-template-visitors":29,"jstransform/visitors/es7-spread-property-visitors":31,"jstransform/visitors/type-syntax":33}]},{},[1])(1) +},{"./transforms/react":34,"./transforms/reactDisplayName":35,"jstransform/visitors/es6-arrow-function-visitors":23,"jstransform/visitors/es6-class-visitors":24,"jstransform/visitors/es6-destructuring-visitors":25,"jstransform/visitors/es6-object-concise-method-visitors":26,"jstransform/visitors/es6-object-short-notation-visitors":27,"jstransform/visitors/es6-rest-param-visitors":28,"jstransform/visitors/es6-template-visitors":29,"jstransform/visitors/es7-spread-property-visitors":31}]},{},[1])(1) }); \ No newline at end of file diff --git a/src/React/Resources/react-with-addons.js b/src/React/Resources/react-with-addons.js index c15f8c6eb..b637678d9 100644 --- a/src/React/Resources/react-with-addons.js +++ b/src/React/Resources/react-with-addons.js @@ -1,5 +1,5 @@ /** - * React (with addons) v0.12.0 + * React (with addons) v0.12.1 */ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(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 Date: Sat, 22 Nov 2014 22:10:19 -0800 Subject: [PATCH 030/859] 1.2.0 release announcement --- build.proj | 4 +-- .../jekyll/_posts/2014-11-22-1.2.0-release.md | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 site/jekyll/_posts/2014-11-22-1.2.0-release.md diff --git a/build.proj b/build.proj index e127dbf17..7e50db9aa 100644 --- a/build.proj +++ b/build.proj @@ -10,8 +10,8 @@ of patent rights can be found in the PATENTS file in the same directory. 1 - 1 - 4 + 2 + 0 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks diff --git a/site/jekyll/_posts/2014-11-22-1.2.0-release.md b/site/jekyll/_posts/2014-11-22-1.2.0-release.md new file mode 100644 index 000000000..17aad6c8e --- /dev/null +++ b/site/jekyll/_posts/2014-11-22-1.2.0-release.md @@ -0,0 +1,27 @@ +--- +title: "ReactJS.NET 1.2.0" +layout: post +author: Daniel Lo Nigro +--- + +I'm happy to announce the release of ReactJS.NET 1.2.0! This is a minor release and includes a number of changes and fixes since version 1.1.3: + + * Basic source map support. Source maps will now be available when accessing .jsx files directly. It's not supported for combined/minified files at the moment since none of the common .NET minifiers support source maps. ([#8](https://github.com/reactjs/React.NET/issues/8)) + * React version upgraded from 0.12.0 to 0.12.1. + +Server-side rendering: + + * The HTML tag used as the component's container can now be changed. Previously it was hardcoded to be a `div`. ([#45](https://github.com/reactjs/React.NET/issues/45)) + * The ID of the container element can now be changed. Previously it was always an auto-generated ID in the format "react1", "react2", etc. ([#50](https://github.com/reactjs/React.NET/issues/50)) + * New `Html.ReactWithInit` helper method to render both the HTML component and its client-side JavaScript initialisation script. This is useful when rendering self-contained partial views containing React components. *Thanks to BartAdv*. ([#42](https://github.com/reactjs/React.NET/pull/42)) + +Under the hood: + + * Allow custom `JsonSerializerSettings` to be provided. *Thanks to BartAdv. ([#43](https://github.com/reactjs/React.NET/pull/43)) + * Switch various methods from `private` to `protected virtual` so they can be overridden. ([#39](https://github.com/reactjs/React.NET/issues/39)) + * Use newer React factory syntax for instantiating components. ([#46](https://github.com/reactjs/React.NET/issues/46)) + +Have fun, and as always, please feel free to send feedback or bug reports +[on GitHub](https://github.com/reactjs/React.NET). + +— Daniel From 98e6387b5644da7368742c42d4d435bf7da56939 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sat, 22 Nov 2014 23:28:07 -0700 Subject: [PATCH 031/859] Fixed bug in tutorial with comment props Changed the 'Author' and 'Text' props to match the sample JSON data and the data returned by the server. --- site/jekyll/getting-started/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/jekyll/getting-started/tutorial.md b/site/jekyll/getting-started/tutorial.md index cbea953fb..032ef4d34 100644 --- a/site/jekyll/getting-started/tutorial.md +++ b/site/jekyll/getting-started/tutorial.md @@ -325,8 +325,8 @@ var CommentList = React.createClass({ render: function() { var commentNodes = this.props.data.map(function (comment) { return ( - - {comment.text} + + {comment.Text} ); }); From cc01351d6e80e77b95205fee6b707704a415341d Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Mon, 24 Nov 2014 22:08:10 -0800 Subject: [PATCH 032/859] Bump version --- build.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.proj b/build.proj index 7e50db9aa..718bccd39 100644 --- a/build.proj +++ b/build.proj @@ -11,7 +11,7 @@ of patent rights can be found in the PATENTS file in the same directory. 1 2 - 0 + 1 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks From fa7ed588fb3af4c852288fbb4c41fca7b78a09ad Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Thu, 27 Nov 2014 15:38:32 -0800 Subject: [PATCH 033/859] [Cassette] Transform all JavaScript files, not just *.jsx files. Closes #52 --- src/Cassette.React/JsxBundleProcessor.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Cassette.React/JsxBundleProcessor.cs b/src/Cassette.React/JsxBundleProcessor.cs index 9dcc381c9..e1521b119 100644 --- a/src/Cassette.React/JsxBundleProcessor.cs +++ b/src/Cassette.React/JsxBundleProcessor.cs @@ -7,7 +7,6 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -using System; using Cassette.BundleProcessing; using Cassette.Scripts; using React; @@ -35,19 +34,16 @@ public JsxBundleProcessor(CassetteSettings settings, IReactEnvironment environme } /// - /// Processes the specified bundle. Adds a for all .jsx files. + /// Processes the specified bundle. Adds a for all files. /// /// The bundle. public void Process(ScriptBundle bundle) { foreach (var asset in bundle.Assets) { - if (asset.Path.EndsWith(".jsx", StringComparison.InvariantCultureIgnoreCase)) - { - asset.AddAssetTransformer( - new CompileAsset(new JsxCompiler(_environment), _settings.SourceDirectory) - ); - } + asset.AddAssetTransformer( + new CompileAsset(new JsxCompiler(_environment), _settings.SourceDirectory) + ); } } } From ff957fc15966b359790661dddcb596db5fa181b8 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sat, 29 Nov 2014 23:09:10 -0800 Subject: [PATCH 034/859] Experimental support for pooling/reusing JavaScript engines to improve performance. References #13 --- .../App_Start/ReactConfig.cs | 1 + .../App_Start/RouteConfig.cs | 1 + .../Core/JavaScriptEngineFactoryTest.cs | 30 +------- src/React.Tests/Core/ReactEnvironmentTest.cs | 6 +- src/React/IJavaScriptEngineFactory.cs | 24 +++++-- src/React/IReactSiteConfiguration.cs | 12 +++- src/React/JavaScriptEngineFactory.cs | 69 ++++++++++++++++--- src/React/React.csproj | 4 ++ src/React/ReactEnvironment.cs | 48 ++++++------- src/React/ReactSiteConfiguration.cs | 19 +++-- src/React/packages.config | 1 + 11 files changed, 137 insertions(+), 78 deletions(-) diff --git a/src/React.Sample.Mvc4/App_Start/ReactConfig.cs b/src/React.Sample.Mvc4/App_Start/ReactConfig.cs index eea13c2de..28b9672fb 100644 --- a/src/React.Sample.Mvc4/App_Start/ReactConfig.cs +++ b/src/React.Sample.Mvc4/App_Start/ReactConfig.cs @@ -17,6 +17,7 @@ public static void Configure() { ReactSiteConfiguration.Configuration .SetUseHarmony(true) + .SetReuseJavaScriptEngines(true) .AddScript("~/Content/Sample.jsx"); } } diff --git a/src/React.Sample.Mvc4/App_Start/RouteConfig.cs b/src/React.Sample.Mvc4/App_Start/RouteConfig.cs index 1ba781370..d141257a8 100644 --- a/src/React.Sample.Mvc4/App_Start/RouteConfig.cs +++ b/src/React.Sample.Mvc4/App_Start/RouteConfig.cs @@ -17,6 +17,7 @@ public class RouteConfig public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); + routes.IgnoreRoute("favicon.ico"); routes.MapRoute( name: "Comments", diff --git a/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs b/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs index f60327de0..719e110e0 100644 --- a/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs +++ b/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs @@ -19,6 +19,7 @@ public class JavaScriptEngineFactoryTest { private JavaScriptEngineFactory CreateFactory() { + var config = new Mock(); var registration = new JavaScriptEngineFactory.Registration { Factory = () => @@ -29,34 +30,7 @@ private JavaScriptEngineFactory CreateFactory() }, Priority = 1 }; - return new JavaScriptEngineFactory(new[] { registration }); - } - - [Test] - public void ShouldCallOnNewEngineWhenCreatingNew() - { - var factory = CreateFactory(); - var called = false; - factory.GetEngineForCurrentThread(engine => - { - Assert.NotNull(engine); - called = true; - }); - factory.DisposeEngineForCurrentThread(); - - Assert.True(called); - } - - [Test] - public void ShouldNotCallOnNewEngineWhenUsingExisting() - { - var factory = CreateFactory(); - var called = false; - factory.GetEngineForCurrentThread(); - factory.GetEngineForCurrentThread(engine => { called = true; }); - factory.DisposeEngineForCurrentThread(); - - Assert.False(called); + return new JavaScriptEngineFactory(new[] { registration }, config.Object); } [Test] diff --git a/src/React.Tests/Core/ReactEnvironmentTest.cs b/src/React.Tests/Core/ReactEnvironmentTest.cs index 2ac433f51..637333e7e 100644 --- a/src/React.Tests/Core/ReactEnvironmentTest.cs +++ b/src/React.Tests/Core/ReactEnvironmentTest.cs @@ -42,7 +42,7 @@ public void ExecuteWithLargerStackIfRequiredWithNewThread() environment.ExecuteWithLargerStackIfRequired("foo"); mocks.EngineFactory.Verify( - x => x.GetEngineForCurrentThread(It.IsAny>()), + x => x.GetEngineForCurrentThread(), Times.Exactly(2), "Two engines should be created (initial thread and new thread)" ); @@ -111,7 +111,7 @@ public Mocks() FileSystem = new Mock(); FileCacheHash = new Mock(); - EngineFactory.Setup(x => x.GetEngineForCurrentThread(It.IsAny>())).Returns(Engine.Object); + EngineFactory.Setup(x => x.GetEngineForCurrentThread()).Returns(Engine.Object); } public ReactEnvironment CreateReactEnvironment() @@ -126,4 +126,4 @@ public ReactEnvironment CreateReactEnvironment() } } } -} +} \ No newline at end of file diff --git a/src/React/IJavaScriptEngineFactory.cs b/src/React/IJavaScriptEngineFactory.cs index 7c4bc60aa..bb29e05eb 100644 --- a/src/React/IJavaScriptEngineFactory.cs +++ b/src/React/IJavaScriptEngineFactory.cs @@ -9,18 +9,28 @@ namespace React public interface IJavaScriptEngineFactory { /// - /// Gets the JavaScript engine for the current thread + /// Gets the JavaScript engine for the current thread. It is recommended to use + /// instead, which will pool/reuse engines. /// - /// - /// Called if a brand new JavaScript engine is being created for this thread. - /// Should handle initialisation. - /// /// The JavaScript engine - IJsEngine GetEngineForCurrentThread(Action onNewEngine); + IJsEngine GetEngineForCurrentThread(); /// - /// Disposes the JavaScript engine for the current thread. + /// Disposes the JavaScript engine for the current thread. This should only be used + /// if the engine was acquired through . /// void DisposeEngineForCurrentThread(); + + /// + /// Gets a JavaScript engine from the pool. + /// + /// The JavaScript engine + IJsEngine GetEngine(); + + /// + /// Returns an engine to the pool so it can be reused + /// + /// Engine to return + void ReturnEngineToPool(IJsEngine engine); } } \ No newline at end of file diff --git a/src/React/IReactSiteConfiguration.cs b/src/React/IReactSiteConfiguration.cs index 3ae60b03a..e00439d29 100644 --- a/src/React/IReactSiteConfiguration.cs +++ b/src/React/IReactSiteConfiguration.cs @@ -44,6 +44,16 @@ public interface IReactSiteConfiguration ///
IReactSiteConfiguration SetUseHarmony(bool useHarmony); + /// + /// Gets or sets whether JavaScript engines should be reused across requests. + /// + /// + bool ReuseJavaScriptEngines { get; set; } + /// + /// Sets whether JavaScript engines should be reused across requests. + /// + IReactSiteConfiguration SetReuseJavaScriptEngines(bool value); + /// /// Gets or sets the configuration for JSON serializer. /// @@ -53,7 +63,7 @@ public interface IReactSiteConfiguration /// Sets the configuration for json serializer. ///
/// - /// Thic confiquration is used when component initialization script + /// This confiquration is used when component initialization script /// is being generated server-side. /// /// The settings. diff --git a/src/React/JavaScriptEngineFactory.cs b/src/React/JavaScriptEngineFactory.cs index f9d9ecdae..f000afafa 100644 --- a/src/React/JavaScriptEngineFactory.cs +++ b/src/React/JavaScriptEngineFactory.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using JavaScriptEngineSwitcher.Core; +using JSPool; using React.Exceptions; namespace React @@ -14,6 +15,10 @@ namespace React ///
public class JavaScriptEngineFactory : IDisposable, IJavaScriptEngineFactory { + /// + /// React configuration for the current site + /// + protected readonly IReactSiteConfiguration _config; /// /// Function used to create new JavaScript engine instances. /// @@ -23,32 +28,56 @@ public class JavaScriptEngineFactory : IDisposable, IJavaScriptEngineFactory ///
protected readonly ConcurrentDictionary _engines = new ConcurrentDictionary(); + /// + /// Pool of JavaScript engines to use + /// + protected readonly IJsPool _pool; /// /// Initializes a new instance of the class. /// - public JavaScriptEngineFactory(IEnumerable availableFactories) + public JavaScriptEngineFactory(IEnumerable availableFactories, IReactSiteConfiguration config) { + _config = config; _factory = GetFactory(availableFactories); + if (_config.ReuseJavaScriptEngines) + { + _pool = new JsPool(new JsPoolConfig + { + EngineFactory = _factory, + Initializer = InitialiseEngine, + }); + } + } + + /// + /// Loads standard React and JSXTransformer scripts into the engine. + /// + protected virtual void InitialiseEngine(IJsEngine engine) + { + var thisAssembly = typeof(ReactEnvironment).Assembly; + engine.ExecuteResource("React.Resources.shims.js", thisAssembly); + engine.ExecuteResource("React.Resources.react-with-addons.js", thisAssembly); + engine.Execute("var React = global.React"); + + // Only load JSX Transformer if engine supports it + if (engine.SupportsJsxTransformer()) + { + engine.ExecuteResource("React.Resources.JSXTransformer.js", thisAssembly); + } } /// - /// Gets the JavaScript engine for the current thread + /// Gets the JavaScript engine for the current thread. It is recommended to use + /// instead, which will pool/reuse engines. /// - /// - /// Called if a brand new JavaScript engine is being created for this thread. - /// Should handle initialisation. - /// /// The JavaScript engine - public virtual IJsEngine GetEngineForCurrentThread(Action onNewEngine = null) + public virtual IJsEngine GetEngineForCurrentThread() { return _engines.GetOrAdd(Thread.CurrentThread.ManagedThreadId, id => { var engine = _factory(); - if (onNewEngine != null) - { - onNewEngine(engine); - } + InitialiseEngine(engine); return engine; }); } @@ -68,6 +97,24 @@ public virtual void DisposeEngineForCurrentThread() } } + /// + /// Gets a JavaScript engine from the pool. + /// + /// The JavaScript engine + public virtual IJsEngine GetEngine() + { + return _pool.GetEngine(); + } + + /// + /// Returns an engine to the pool so it can be reused + /// + /// Engine to return + public virtual void ReturnEngineToPool(IJsEngine engine) + { + _pool.ReturnEngineToPool(engine); + } + /// /// Gets a factory for the most appropriate JavaScript engine for the current environment. /// The first functioning JavaScript engine with the lowest priority will be used. diff --git a/src/React/React.csproj b/src/React/React.csproj index 7b09c1882..20195c649 100644 --- a/src/React/React.csproj +++ b/src/React/React.csproj @@ -62,6 +62,10 @@ ..\packages\JavaScriptEngineSwitcher.Jint.1.2.1\lib\net40\Jint.dll True + + False + ..\packages\JSPool.0.1.1\lib\net40-Client\JSPool.dll + ..\packages\MsieJavaScriptEngine.1.5.0\lib\net40\MsieJavaScriptEngine.dll True diff --git a/src/React/ReactEnvironment.cs b/src/React/ReactEnvironment.cs index b497938ee..109bc8a6e 100644 --- a/src/React/ReactEnvironment.cs +++ b/src/React/ReactEnvironment.cs @@ -68,7 +68,12 @@ public class ReactEnvironment : IReactEnvironment, IDisposable /// /// Version number of ReactJS.NET /// - protected readonly Lazy _version = new Lazy(GetVersion); + protected readonly Lazy _version = new Lazy(GetVersion); + /// + /// Contains an engine acquired from a pool of engines. Only used if + /// is enabled. + /// + protected readonly Lazy _engineFromPool; /// /// Number of components instantiated in this environment @@ -103,17 +108,19 @@ IFileCacheHash fileCacheHash _jsxTransformer = new Lazy(() => new JsxTransformer(this, _cache, _fileSystem, _fileCacheHash, _config) ); + _engineFromPool = new Lazy(() => _engineFactory.GetEngine()); } /// - /// Gets the JavaScript engine for the current thread. If an engine has not yet been - /// created, create it and execute the startup scripts. + /// Gets the JavaScript engine to use for this environment. /// protected virtual IJsEngine Engine { get { - return _engineFactory.GetEngineForCurrentThread(InitialiseEngine); + return _config.ReuseJavaScriptEngines + ? _engineFromPool.Value + : _engineFactory.GetEngineForCurrentThread(); } } @@ -149,23 +156,6 @@ public virtual string Version get { return _version.Value; } } - /// - /// Loads standard React and JSXTransformer scripts into the engine. - /// - protected virtual void InitialiseEngine(IJsEngine engine) - { - var thisAssembly = typeof(ReactEnvironment).Assembly; - engine.ExecuteResource("React.Resources.shims.js", thisAssembly); - engine.ExecuteResource("React.Resources.react-with-addons.js", thisAssembly); - engine.Execute("var React = global.React"); - - // Only load JSX Transformer if engine supports it - if (engine.SupportsJsxTransformer()) - { - engine.ExecuteResource("React.Resources.JSXTransformer.js", thisAssembly); - } - } - /// /// Ensures any user-provided scripts have been loaded /// @@ -362,6 +352,13 @@ public string TransformJsx(string input) /// Result returned from JavaScript code public virtual T ExecuteWithLargerStackIfRequired(string function, params object[] args) { + // This hack is not required when pooling JavaScript engines, since pooled MSIE + // engines already execute on their own thread with a larger stack. + if (_config.ReuseJavaScriptEngines) + { + return Execute(function, args); + } + try { return Execute(function, args); @@ -375,10 +372,11 @@ public virtual T ExecuteWithLargerStackIfRequired(string function, params obj Exception innerEx = null; var thread = new Thread(() => { + // New engine will be created here (as this is a new thread) + var engine = _engineFactory.GetEngineForCurrentThread(); try { - // New engine will be created here (as this is a new thread) - result = Execute(function, args); + result = engine.CallFunction(function, args); } catch (Exception threadEx) { @@ -421,6 +419,10 @@ private static string GetVersion() public virtual void Dispose() { _engineFactory.DisposeEngineForCurrentThread(); + if (_engineFromPool.IsValueCreated) + { + _engineFactory.ReturnEngineToPool(_engineFromPool.Value); + } } /// diff --git a/src/React/ReactSiteConfiguration.cs b/src/React/ReactSiteConfiguration.cs index ec65b10f0..a06af5fb8 100644 --- a/src/React/ReactSiteConfiguration.cs +++ b/src/React/ReactSiteConfiguration.cs @@ -74,11 +74,7 @@ public IReactSiteConfiguration SetUseHarmony(bool useHarmony) /// /// Gets or sets the configuration for JSON serializer. /// - public JsonSerializerSettings JsonSerializerSettings - { - get; - set; - } + public JsonSerializerSettings JsonSerializerSettings { get; set; } /// /// Sets the configuration for json serializer. @@ -93,5 +89,18 @@ public IReactSiteConfiguration SetJsonSerializerSettings(JsonSerializerSettings JsonSerializerSettings = settings; return this; } + + /// + /// Gets or sets whether JavaScript engines should be reused across requests. + /// + public bool ReuseJavaScriptEngines { get; set; } + /// + /// Sets whether JavaScript engines should be reused across requests. + /// + public IReactSiteConfiguration SetReuseJavaScriptEngines(bool value) + { + ReuseJavaScriptEngines = value; + return this; + } } } diff --git a/src/React/packages.config b/src/React/packages.config index 8af52c8f0..37300c272 100644 --- a/src/React/packages.config +++ b/src/React/packages.config @@ -3,6 +3,7 @@ + \ No newline at end of file From f65bfc455e9a49cc7329ffe3e02975fedf188dc6 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 16:53:36 -0800 Subject: [PATCH 035/859] Recycle the JavaScript engine pool if any of the loaded files are changed --- .../Core/JavaScriptEngineFactoryTest.cs | 3 +- src/React/JavaScriptEngineFactory.cs | 132 +++++++++++++++++- 2 files changed, 127 insertions(+), 8 deletions(-) diff --git a/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs b/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs index 719e110e0..5a1c4da76 100644 --- a/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs +++ b/src/React.Tests/Core/JavaScriptEngineFactoryTest.cs @@ -20,6 +20,7 @@ public class JavaScriptEngineFactoryTest private JavaScriptEngineFactory CreateFactory() { var config = new Mock(); + var fileSystem = new Mock(); var registration = new JavaScriptEngineFactory.Registration { Factory = () => @@ -30,7 +31,7 @@ private JavaScriptEngineFactory CreateFactory() }, Priority = 1 }; - return new JavaScriptEngineFactory(new[] { registration }, config.Object); + return new JavaScriptEngineFactory(new[] { registration }, config.Object, fileSystem.Object); } [Test] diff --git a/src/React/JavaScriptEngineFactory.cs b/src/React/JavaScriptEngineFactory.cs index f000afafa..968f8e289 100644 --- a/src/React/JavaScriptEngineFactory.cs +++ b/src/React/JavaScriptEngineFactory.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading; using JavaScriptEngineSwitcher.Core; @@ -20,6 +21,10 @@ public class JavaScriptEngineFactory : IDisposable, IJavaScriptEngineFactory /// protected readonly IReactSiteConfiguration _config; /// + /// File system wrapper + /// + protected readonly IFileSystem _fileSystem; + /// /// Function used to create new JavaScript engine instances. /// protected readonly Func _factory; @@ -31,25 +36,84 @@ protected readonly ConcurrentDictionary _engines /// /// Pool of JavaScript engines to use /// - protected readonly IJsPool _pool; + protected IJsPool _pool; + /// + /// Used to recycle the JavaScript engine pool when relevant JavaScript files are modified. + /// + protected readonly FileSystemWatcher _watcher; + /// + /// Names of all the files that are loaded into the JavaScript engine. If any of these + /// files are changed, the engines should be recycled + /// + protected readonly ISet _watchedFiles; + /// + /// Timer for debouncing pool recycling + /// + protected readonly Timer _resetPoolTimer; + /// + /// Whether this class has been disposed. + /// + protected bool _disposed; + + /// + /// Time period to debounce file system changed events, in milliseconds. + /// + protected const int DEBOUNCE_TIMEOUT = 25; /// /// Initializes a new instance of the class. /// - public JavaScriptEngineFactory(IEnumerable availableFactories, IReactSiteConfiguration config) + public JavaScriptEngineFactory( + IEnumerable availableFactories, + IReactSiteConfiguration config, + IFileSystem fileSystem + ) { _config = config; + _fileSystem = fileSystem; _factory = GetFactory(availableFactories); if (_config.ReuseJavaScriptEngines) { - _pool = new JsPool(new JsPoolConfig + _pool = CreatePool(); + _resetPoolTimer = new Timer(OnResetPoolTimer); + _watchedFiles = new HashSet(_config.Scripts.Select( + fileName => _fileSystem.MapPath(fileName).ToLowerInvariant() + )); + try { - EngineFactory = _factory, - Initializer = InitialiseEngine, - }); + // Attempt to initialise a FileSystemWatcher so we can recycle the JavaScript + // engine pool when files are changed. + _watcher = new FileSystemWatcher + { + Path = _fileSystem.MapPath("~"), + IncludeSubdirectories = true, + EnableRaisingEvents = true, + }; + _watcher.Changed += OnFileChanged; + _watcher.Created += OnFileChanged; + _watcher.Deleted += OnFileChanged; + _watcher.Renamed += OnFileChanged; + } + catch (Exception ex) + { + // Can't use FileSystemWatcher (eg. not running in Full Trust) + Trace.WriteLine("Unable to initialise FileSystemWatcher: " + ex.Message); + } } } + /// + /// Creates a new JavaScript engine pool. + /// + protected virtual IJsPool CreatePool() + { + return new JsPool(new JsPoolConfig + { + EngineFactory = _factory, + Initializer = InitialiseEngine, + }); + } + /// /// Loads standard React and JSXTransformer scripts into the engine. /// @@ -74,6 +138,7 @@ protected virtual void InitialiseEngine(IJsEngine engine) /// The JavaScript engine public virtual IJsEngine GetEngineForCurrentThread() { + EnsureNotDisposed(); return _engines.GetOrAdd(Thread.CurrentThread.ManagedThreadId, id => { var engine = _factory(); @@ -103,6 +168,7 @@ public virtual void DisposeEngineForCurrentThread() /// The JavaScript engine public virtual IJsEngine GetEngine() { + EnsureNotDisposed(); return _pool.GetEngine(); } @@ -112,7 +178,12 @@ public virtual IJsEngine GetEngine() /// Engine to return public virtual void ReturnEngineToPool(IJsEngine engine) { - _pool.ReturnEngineToPool(engine); + // This could be called from ReactEnvironment.Dispose if that class is disposed after + // this class. Let's just ignore this if it's disposed. + if (!_disposed) + { + _pool.ReturnEngineToPool(engine); + } } /// @@ -161,6 +232,7 @@ private static Func GetFactory(IEnumerable availableFac /// public virtual void Dispose() { + _disposed = true; foreach (var engine in _engines) { if (engine.Value != null) @@ -168,6 +240,52 @@ public virtual void Dispose() engine.Value.Dispose(); } } + if (_pool != null) + { + _pool.Dispose(); + _pool = null; + } + } + + /// + /// Handles events fired when any files are changed. + /// + /// The sender + /// The instance containing the event data + protected virtual void OnFileChanged(object sender, FileSystemEventArgs args) + { + if (_watchedFiles.Contains(args.FullPath.ToLowerInvariant())) + { + // Use a timer so multiple changes only result in a single reset. + _resetPoolTimer.Change(DEBOUNCE_TIMEOUT, Timeout.Infinite); + } + } + + /// + /// Called when any of the watched files have changed. Recycles the JavaScript engine pool + /// so the files are all reloaded. + /// + /// Unused + protected virtual void OnResetPoolTimer(object state) + { + // Create the new pool before disposing the old pool so that _pool is never null. + var oldPool = _pool; + _pool = CreatePool(); + if (oldPool != null) + { + oldPool.Dispose(); + } + } + + /// + /// Ensures that this object has not been disposed. + /// + public void EnsureNotDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } } /// From 71d0cf4edf3f57a3b1948a51271402407b620086 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 17:16:51 -0800 Subject: [PATCH 036/859] Enable harmony (ES6) and JavaScript engine pooling by default --- src/React.Tests/Core/JsxTransformerTests.cs | 10 +++++----- src/React/JsxTransformer.cs | 1 - src/React/ReactSiteConfiguration.cs | 9 +++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/React.Tests/Core/JsxTransformerTests.cs b/src/React.Tests/Core/JsxTransformerTests.cs index 1b4358f57..22ee12e6b 100644 --- a/src/React.Tests/Core/JsxTransformerTests.cs +++ b/src/React.Tests/Core/JsxTransformerTests.cs @@ -53,7 +53,7 @@ public void ShouldTransformJsx() _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", "
Hello World
", - false + It.IsAny() )); } @@ -63,7 +63,7 @@ public void ShouldWrapExceptionsInJsxExeption() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", "
Hello World
", - false + It.IsAny() )).Throws(new Exception("Something broke...")); const string input = "
Hello World
"; @@ -116,7 +116,7 @@ public void ShouldTransformJsxIfFileCacheHashInvalid() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", It.IsAny(), - false + It.IsAny() )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); var result = _jsxTransformer.TransformJsxFile("foo.jsx"); @@ -132,7 +132,7 @@ public void ShouldTransformJsxIfNoCache() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", It.IsAny(), - false + It.IsAny() )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); var result = _jsxTransformer.TransformJsxFile("foo.jsx"); @@ -146,7 +146,7 @@ public void ShouldSaveTransformationResult() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", It.IsAny(), - false + It.IsAny() )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); string result = null; diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index 97f8465d8..09beb0ca5 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -219,7 +219,6 @@ protected virtual JavaScriptWithSourceMap TransformJsxWithHeader(string filename hash = _fileCacheHash.CalculateHash(contents); } var header = GetFileHeader(hash); - //var result = TransformJsxWithSourceMap(header + contents, useHarmony); var result = TransformJsxWithSourceMap(header + contents, useHarmony); result.Hash = hash; if (result.SourceMap != null) diff --git a/src/React/ReactSiteConfiguration.cs b/src/React/ReactSiteConfiguration.cs index a06af5fb8..89cd2f8b8 100644 --- a/src/React/ReactSiteConfiguration.cs +++ b/src/React/ReactSiteConfiguration.cs @@ -28,6 +28,15 @@ static ReactSiteConfiguration() Configuration = new ReactSiteConfiguration(); } + /// + /// Initializes a new instance of the class. + /// + public ReactSiteConfiguration() + { + UseHarmony = true; + ReuseJavaScriptEngines = true; + } + /// /// All the scripts that have been added to this configuration /// From 8f642b1b7a50925ea0ee6646a2d8478fa5888b73 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 17:36:42 -0800 Subject: [PATCH 037/859] Add ability to strip out Flow type annotations --- src/React.MSBuild/TransformJsx.cs | 6 ++ .../App_Start/ReactConfig.cs | 1 + src/React.Sample.Mvc4/TransformJsx.proj | 2 +- src/React.Tests/Core/JsxTransformerTests.cs | 15 ++-- src/React/IJsxTransformer.cs | 35 ++++++++-- src/React/IReactSiteConfiguration.cs | 9 +++ src/React/JsxTransformer.cs | 69 +++++++++++++++---- src/React/ReactSiteConfiguration.cs | 13 ++++ src/React/Resources/shims.js | 10 ++- 9 files changed, 134 insertions(+), 26 deletions(-) diff --git a/src/React.MSBuild/TransformJsx.cs b/src/React.MSBuild/TransformJsx.cs index f9f5c72d4..2cbadb4cb 100644 --- a/src/React.MSBuild/TransformJsx.cs +++ b/src/React.MSBuild/TransformJsx.cs @@ -36,6 +36,11 @@ public class TransformJsx : Task /// true if support for es6 syntax should be rewritten. public bool UseHarmony { get; set; } + /// + /// Gets or sets whether Flow types should be stripped. + /// + public bool StripTypes { get; set; } + /// /// Executes the task. /// @@ -45,6 +50,7 @@ public override bool Execute() MSBuildHost.EnsureInitialized(); var config = React.AssemblyRegistration.Container.Resolve(); config.UseHarmony = UseHarmony; + config.StripTypes = StripTypes; _environment = React.AssemblyRegistration.Container.Resolve(); diff --git a/src/React.Sample.Mvc4/App_Start/ReactConfig.cs b/src/React.Sample.Mvc4/App_Start/ReactConfig.cs index 28b9672fb..6b7ae4a2d 100644 --- a/src/React.Sample.Mvc4/App_Start/ReactConfig.cs +++ b/src/React.Sample.Mvc4/App_Start/ReactConfig.cs @@ -18,6 +18,7 @@ public static void Configure() ReactSiteConfiguration.Configuration .SetUseHarmony(true) .SetReuseJavaScriptEngines(true) + .SetStripTypes(true) .AddScript("~/Content/Sample.jsx"); } } diff --git a/src/React.Sample.Mvc4/TransformJsx.proj b/src/React.Sample.Mvc4/TransformJsx.proj index b51181034..e4eac8876 100644 --- a/src/React.Sample.Mvc4/TransformJsx.proj +++ b/src/React.Sample.Mvc4/TransformJsx.proj @@ -2,6 +2,6 @@ - + \ No newline at end of file diff --git a/src/React.Tests/Core/JsxTransformerTests.cs b/src/React.Tests/Core/JsxTransformerTests.cs index 22ee12e6b..433a5cf94 100644 --- a/src/React.Tests/Core/JsxTransformerTests.cs +++ b/src/React.Tests/Core/JsxTransformerTests.cs @@ -53,7 +53,8 @@ public void ShouldTransformJsx() _environment.Verify(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", "
Hello World
", - It.IsAny() + It.IsAny(), + It.IsAny() )); } @@ -63,7 +64,8 @@ public void ShouldWrapExceptionsInJsxExeption() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform", "
Hello World
", - It.IsAny() + It.IsAny(), + It.IsAny() )).Throws(new Exception("Something broke...")); const string input = "
Hello World
"; @@ -116,7 +118,8 @@ public void ShouldTransformJsxIfFileCacheHashInvalid() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", It.IsAny(), - It.IsAny() + It.IsAny(), + It.IsAny() )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); var result = _jsxTransformer.TransformJsxFile("foo.jsx"); @@ -132,7 +135,8 @@ public void ShouldTransformJsxIfNoCache() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", It.IsAny(), - It.IsAny() + It.IsAny(), + It.IsAny() )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); var result = _jsxTransformer.TransformJsxFile("foo.jsx"); @@ -146,7 +150,8 @@ public void ShouldSaveTransformationResult() _environment.Setup(x => x.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", It.IsAny(), - It.IsAny() + It.IsAny(), + It.IsAny() )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); string result = null; diff --git a/src/React/IJsxTransformer.cs b/src/React/IJsxTransformer.cs index ee9b475ce..8c4caa320 100644 --- a/src/React/IJsxTransformer.cs +++ b/src/React/IJsxTransformer.cs @@ -19,8 +19,12 @@ public interface IJsxTransformer ///
/// Name of the file to load /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript - string TransformJsxFile(string filename, bool? useHarmony = null); + string TransformJsxFile(string filename, bool? useHarmony = null, bool? stripTypes = null); /// /// Transforms a JSX file to regular JavaScript and also returns a source map to map the @@ -32,8 +36,17 @@ public interface IJsxTransformer /// true to re-transform the file if a cached version with no source map is available /// /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript and source map - JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bool forceGenerateSourceMap = false, bool? useHarmony = null); + JavaScriptWithSourceMap TransformJsxFileWithSourceMap( + string filename, + bool forceGenerateSourceMap = false, + bool? useHarmony = null, + bool? stripTypes = null + ); /// /// Transforms JSX into regular JavaScript. The result is not cached. Use @@ -41,8 +54,12 @@ public interface IJsxTransformer /// /// JSX /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript - string TransformJsx(string input, bool? useHarmony = null); + string TransformJsx(string input, bool? useHarmony = null, bool? stripTypes = null); /// /// Transforms JSX to regular JavaScript and also returns a source map to map the compiled @@ -50,8 +67,12 @@ public interface IJsxTransformer /// /// JSX /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript and source map - JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony = null); + JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony = null, bool? stripTypes = null); /// /// Transforms a JSX file to JavaScript, and saves the result into a ".generated.js" file @@ -59,8 +80,12 @@ public interface IJsxTransformer /// /// Name of the file to load /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// File contents - string TransformAndSaveJsxFile(string filename, bool? useHarmony = null); + string TransformAndSaveJsxFile(string filename, bool? useHarmony = null, bool? stripTypes = null); /// /// Returns the path the specified JSX file's compilation will be cached to diff --git a/src/React/IReactSiteConfiguration.cs b/src/React/IReactSiteConfiguration.cs index e00439d29..54f00a8e7 100644 --- a/src/React/IReactSiteConfiguration.cs +++ b/src/React/IReactSiteConfiguration.cs @@ -68,5 +68,14 @@ public interface IReactSiteConfiguration /// /// The settings. IReactSiteConfiguration SetJsonSerializerSettings(JsonSerializerSettings settings); + + /// + /// Gets or sets whether Flow types should be stripped out. + /// + bool StripTypes { get; set; } + /// + /// Sets whether Flow types should be stripped out. + /// + IReactSiteConfiguration SetStripTypes(bool stripTypes); } } diff --git a/src/React/JsxTransformer.cs b/src/React/JsxTransformer.cs index 09beb0ca5..dac456fca 100644 --- a/src/React/JsxTransformer.cs +++ b/src/React/JsxTransformer.cs @@ -79,10 +79,14 @@ public JsxTransformer(IReactEnvironment environment, ICache cache, IFileSystem f /// /// Name of the file to load /// true if support for es6 syntax should be rewritten. + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript - public virtual string TransformJsxFile(string filename, bool? useHarmony = null) + public virtual string TransformJsxFile(string filename, bool? useHarmony = null, bool? stripTypes = null) { - return TransformJsxFileWithSourceMap(filename, false, useHarmony).Code; + return TransformJsxFileWithSourceMap(filename, false, useHarmony, stripTypes).Code; } /// @@ -95,8 +99,17 @@ public virtual string TransformJsxFile(string filename, bool? useHarmony = null) /// true to re-transform the file if a cached version with no source map is available /// /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript and source map - public virtual JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string filename, bool forceGenerateSourceMap = false, bool? useHarmony = null) + public virtual JavaScriptWithSourceMap TransformJsxFileWithSourceMap( + string filename, + bool forceGenerateSourceMap = false, + bool? useHarmony = null, + bool? stripTypes = null + ) { var cacheKey = string.Format(JSX_CACHE_KEY, filename); @@ -118,7 +131,7 @@ public virtual JavaScriptWithSourceMap TransformJsxFileWithSourceMap(string file // 3. Not cached, perform the transformation try { - output = TransformJsxWithHeader(filename, contents, hash, useHarmony); + output = TransformJsxWithHeader(filename, contents, hash, useHarmony, stripTypes); } catch (JsxException ex) { @@ -211,15 +224,25 @@ protected virtual JavaScriptWithSourceMap LoadJsxFromFileCache(string filename, /// Contents of the input file /// Hash of the input. If null, it will be calculated /// true if support for es6 syntax should be rewritten. + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript - protected virtual JavaScriptWithSourceMap TransformJsxWithHeader(string filename, string contents, string hash = null, bool? useHarmony = null) + protected virtual JavaScriptWithSourceMap TransformJsxWithHeader( + string filename, + string contents, + string hash = null, + bool? useHarmony = null, + bool? stripTypes = null + ) { if (string.IsNullOrEmpty(hash)) { hash = _fileCacheHash.CalculateHash(contents); } var header = GetFileHeader(hash); - var result = TransformJsxWithSourceMap(header + contents, useHarmony); + var result = TransformJsxWithSourceMap(header + contents, useHarmony, stripTypes); result.Hash = hash; if (result.SourceMap != null) { @@ -240,8 +263,12 @@ protected virtual JavaScriptWithSourceMap TransformJsxWithHeader(string filename /// /// JSX /// true if support for es6 syntax should be rewritten. + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript - public virtual string TransformJsx(string input, bool? useHarmony = null) + public virtual string TransformJsx(string input, bool? useHarmony = null, bool? stripTypes = null) { EnsureJsxTransformerSupported(); try @@ -249,7 +276,8 @@ public virtual string TransformJsx(string input, bool? useHarmony = null) var output = _environment.ExecuteWithLargerStackIfRequired( "ReactNET_transform", input, - useHarmony ?? _config.UseHarmony + useHarmony ?? _config.UseHarmony, + stripTypes ?? _config.StripTypes ); return output; } @@ -265,8 +293,16 @@ public virtual string TransformJsx(string input, bool? useHarmony = null) /// /// JSX /// true if support for ES6 syntax should be enabled + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// JavaScript and source map - public virtual JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, bool? useHarmony) + public virtual JavaScriptWithSourceMap TransformJsxWithSourceMap( + string input, + bool? useHarmony = null, + bool? stripTypes = null + ) { EnsureJsxTransformerSupported(); try @@ -274,7 +310,8 @@ public virtual JavaScriptWithSourceMap TransformJsxWithSourceMap(string input, b return _environment.ExecuteWithLargerStackIfRequired( "ReactNET_transform_sourcemap", input, - useHarmony ?? _config.UseHarmony + useHarmony ?? _config.UseHarmony, + stripTypes ?? _config.StripTypes ); } catch (Exception ex) @@ -331,13 +368,21 @@ public virtual string GetSourceMapOutputPath(string path) ///
/// Name of the file to load /// true if support for es6 syntax should be rewritten. + /// + /// Whether Flow types should be stripped out. Defaults to the value set in the site + /// configuration. + /// /// File contents - public virtual string TransformAndSaveJsxFile(string filename, bool? useHarmony = null) + public virtual string TransformAndSaveJsxFile( + string filename, + bool? useHarmony = null, + bool? stripTypes = null + ) { var outputPath = GetJsxOutputPath(filename); var sourceMapPath = GetSourceMapOutputPath(filename); var contents = _fileSystem.ReadAsString(filename); - var result = TransformJsxWithHeader(filename, contents, useHarmony: useHarmony); + var result = TransformJsxWithHeader(filename, contents, null, useHarmony, stripTypes); _fileSystem.WriteAsString(outputPath, result.Code); _fileSystem.WriteAsString(sourceMapPath, result.SourceMap == null ? string.Empty : result.SourceMap.ToJson()); return outputPath; diff --git a/src/React/ReactSiteConfiguration.cs b/src/React/ReactSiteConfiguration.cs index 89cd2f8b8..9e70e8c40 100644 --- a/src/React/ReactSiteConfiguration.cs +++ b/src/React/ReactSiteConfiguration.cs @@ -111,5 +111,18 @@ public IReactSiteConfiguration SetReuseJavaScriptEngines(bool value) ReuseJavaScriptEngines = value; return this; } + + /// + /// Gets or sets whether Flow types should be stripped. + /// + public bool StripTypes { get; set; } + /// + /// Sets whether Flow types should be stripped + /// + public IReactSiteConfiguration SetStripTypes(bool stripTypes) + { + StripTypes = stripTypes; + return this; + } } } diff --git a/src/React/Resources/shims.js b/src/React/Resources/shims.js index f8379a65e..9e717853d 100644 --- a/src/React/Resources/shims.js +++ b/src/React/Resources/shims.js @@ -19,18 +19,22 @@ if (!Object.freeze) { Object.freeze = function() { }; } -function ReactNET_transform(input, harmony) { +function ReactNET_transform(input, harmony, stripTypes) { try { - return global.JSXTransformer.transform(input, { harmony: !!harmony }).code; + return global.JSXTransformer.transform(input, { + harmony: !!harmony, + stripTypes: !!stripTypes + }).code; } catch (ex) { throw new Error(ex.message + " (at line " + ex.lineNumber + " column " + ex.column + ")"); } } -function ReactNET_transform_sourcemap(input, harmony) { +function ReactNET_transform_sourcemap(input, harmony, stripTypes) { try { var result = global.JSXTransformer.transform(input, { harmony: !!harmony, + stripTypes: !!stripTypes, sourceMap: true }); if (!result.sourceMap) { From c467869f9d0763068b1a6917ae2812eb08be2621 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 17:42:59 -0800 Subject: [PATCH 038/859] Bump version number to 1.3.0 --- build.proj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.proj b/build.proj index 718bccd39..07f559ad2 100644 --- a/build.proj +++ b/build.proj @@ -10,8 +10,8 @@ of patent rights can be found in the PATENTS file in the same directory. 1 - 2 - 1 + 3 + 0 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks From a036b92bcdaf05b9e3ba6fb8b821f3e8b19c638d Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 17:50:59 -0800 Subject: [PATCH 039/859] Allow configuration of number of JavaScript engines in the pool in IReactSiteConfiguration --- src/React/IReactSiteConfiguration.cs | 22 ++++++++++++++++++++ src/React/JavaScriptEngineFactory.cs | 14 +++++++++++-- src/React/ReactSiteConfiguration.cs | 30 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/React/IReactSiteConfiguration.cs b/src/React/IReactSiteConfiguration.cs index 54f00a8e7..c6975004c 100644 --- a/src/React/IReactSiteConfiguration.cs +++ b/src/React/IReactSiteConfiguration.cs @@ -77,5 +77,27 @@ public interface IReactSiteConfiguration /// Sets whether Flow types should be stripped out. ///
IReactSiteConfiguration SetStripTypes(bool stripTypes); + + /// + /// Gets or sets the number of engines to initially start when a pool is created. + /// Defaults to 10. + /// + int? StartEngines { get; set; } + /// + /// Sets the number of engines to initially start when a pool is created. + /// Defaults to 10. + /// + IReactSiteConfiguration SetStartEngines(int? startEngines); + + /// + /// Gets or sets the maximum number of engines that will be created in the pool. + /// Defaults to 25. + /// + int? MaxEngines { get; set; } + /// + /// Sets the maximum number of engines that will be created in the pool. + /// Defaults to 25. + /// + IReactSiteConfiguration SetMaxEngines(int? maxEngines); } } diff --git a/src/React/JavaScriptEngineFactory.cs b/src/React/JavaScriptEngineFactory.cs index 968f8e289..8cc5c9563 100644 --- a/src/React/JavaScriptEngineFactory.cs +++ b/src/React/JavaScriptEngineFactory.cs @@ -107,11 +107,21 @@ IFileSystem fileSystem ///
protected virtual IJsPool CreatePool() { - return new JsPool(new JsPoolConfig + var poolConfig = new JsPoolConfig { EngineFactory = _factory, Initializer = InitialiseEngine, - }); + }; + if (_config.MaxEngines != null) + { + poolConfig.MaxEngines = _config.MaxEngines.Value; + } + if (_config.StartEngines != null) + { + poolConfig.StartEngines = _config.StartEngines.Value; + } + + return new JsPool(poolConfig); } /// diff --git a/src/React/ReactSiteConfiguration.cs b/src/React/ReactSiteConfiguration.cs index 9e70e8c40..01953c250 100644 --- a/src/React/ReactSiteConfiguration.cs +++ b/src/React/ReactSiteConfiguration.cs @@ -124,5 +124,35 @@ public IReactSiteConfiguration SetStripTypes(bool stripTypes) StripTypes = stripTypes; return this; } + + /// + /// Gets or sets the number of engines to initially start when a pool is created. + /// Defaults to 10. + /// + public int? StartEngines { get; set; } + /// + /// Sets the number of engines to initially start when a pool is created. + /// Defaults to 10. + /// + public IReactSiteConfiguration SetStartEngines(int? startEngines) + { + StartEngines = startEngines; + return this; + } + + /// + /// Gets or sets the maximum number of engines that will be created in the pool. + /// Defaults to 25. + /// + public int? MaxEngines { get; set; } + /// + /// Sets the maximum number of engines that will be created in the pool. + /// Defaults to 25. + /// + public IReactSiteConfiguration SetMaxEngines(int? maxEngines) + { + MaxEngines = maxEngines; + return this; + } } } From c99536bad198e1cd9a35d5cb2303fd541d64a15a Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 17:51:28 -0800 Subject: [PATCH 040/859] Disable pooling in MSBuild as it's not really needed there --- src/React.MSBuild/TransformJsx.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/React.MSBuild/TransformJsx.cs b/src/React.MSBuild/TransformJsx.cs index 2cbadb4cb..d6fa39981 100644 --- a/src/React.MSBuild/TransformJsx.cs +++ b/src/React.MSBuild/TransformJsx.cs @@ -49,8 +49,10 @@ public override bool Execute() { MSBuildHost.EnsureInitialized(); var config = React.AssemblyRegistration.Container.Resolve(); - config.UseHarmony = UseHarmony; - config.StripTypes = StripTypes; + config + .SetReuseJavaScriptEngines(false) + .SetUseHarmony(UseHarmony) + .SetStripTypes(StripTypes); _environment = React.AssemblyRegistration.Container.Resolve(); From 517e96e1619d074d4a2f6fd4e748f778728cdf3c Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 19:05:54 -0800 Subject: [PATCH 041/859] Update default config file to mention ES6 transformations are turned on by default, and added a comment about Flow. --- src/React.Web/Content/App_Start/ReactConfig.cs.pp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/React.Web/Content/App_Start/ReactConfig.cs.pp b/src/React.Web/Content/App_Start/ReactConfig.cs.pp index 0f1a806fa..c3f28798e 100644 --- a/src/React.Web/Content/App_Start/ReactConfig.cs.pp +++ b/src/React.Web/Content/App_Start/ReactConfig.cs.pp @@ -8,9 +8,13 @@ { public static void Configure() { - // If you want to use fancy new ES6 features, uncomment this line: + // ES6 features are enabled by default. Uncomment the below line to disable them. // See http://reactjs.net/guides/es6.html for more information. - //ReactSiteConfiguration.Configuration.SetUseHarmony(true); + //ReactSiteConfiguration.Configuration.SetUseHarmony(false); + + // Uncomment the below line if you are using Flow + // See http://reactjs.net/guides/flow.html for more information. + //ReactSiteConfiguration.Configuration.SetStripTypes(true); // If you want to use server-side rendering of React components, // add all the necessary JavaScript files here. This includes From dd748ccfcddbd472bd4b62d5d2a0b0a804fb9721 Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 20:18:13 -0800 Subject: [PATCH 042/859] 1.3 release announcement --- .../jekyll/_posts/2014-11-22-1.2.0-release.md | 4 ++-- .../jekyll/_posts/2014-11-30-1.3.0-release.md | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 site/jekyll/_posts/2014-11-30-1.3.0-release.md diff --git a/site/jekyll/_posts/2014-11-22-1.2.0-release.md b/site/jekyll/_posts/2014-11-22-1.2.0-release.md index 17aad6c8e..35a45ea42 100644 --- a/site/jekyll/_posts/2014-11-22-1.2.0-release.md +++ b/site/jekyll/_posts/2014-11-22-1.2.0-release.md @@ -1,10 +1,10 @@ --- -title: "ReactJS.NET 1.2.0" +title: "ReactJS.NET 1.2" layout: post author: Daniel Lo Nigro --- -I'm happy to announce the release of ReactJS.NET 1.2.0! This is a minor release and includes a number of changes and fixes since version 1.1.3: +I'm happy to announce the release of ReactJS.NET 1.2! This is a minor release and includes a number of changes and fixes since version 1.1.3: * Basic source map support. Source maps will now be available when accessing .jsx files directly. It's not supported for combined/minified files at the moment since none of the common .NET minifiers support source maps. ([#8](https://github.com/reactjs/React.NET/issues/8)) * React version upgraded from 0.12.0 to 0.12.1. diff --git a/site/jekyll/_posts/2014-11-30-1.3.0-release.md b/site/jekyll/_posts/2014-11-30-1.3.0-release.md new file mode 100644 index 000000000..e91a9836c --- /dev/null +++ b/site/jekyll/_posts/2014-11-30-1.3.0-release.md @@ -0,0 +1,19 @@ +--- +title: "ReactJS.NET 1.3 - Faster server-side rendering and Flow" +layout: post +author: Daniel Lo Nigro +--- + +Hot on the heels of ReactJS.NET 1.2, I'm happy to announce the release of ReactJS.NET 1.3! This release significantly improves the performance of server-side rendering by pooling and reusing JavaScript engines rather than creating a new one on every request. Pooling of JavaScript engines is turned on by default but it can be disabled in your site's configuration (usually `ReactConfig.cs`) to revert back to the classic behaviour. This is still experimental, so please let me know if it works well for you! + +This release also adds support for stripping out [Flow](http://flowtype.org/) type annotations from your code. Flow is a new open-source static type checker for JavaScript, recently released by Facebook. It adds static typing to JavaScript to improve developer productivity and code quality. You can learn more about Flow in [its release announcement](https://code.facebook.com/posts/1505962329687926/flow-a-new-static-type-checker-for-javascript/). Support for Flow is disabled by default but can be enabled in your site's configuration. + +Other changes: + + * [Harmony (ES6)](/guides/es6.html) transformations are enabled by default. They can be disabled in your site's configuration. + * The Cassette integration now transforms all files in the bundle, not just `*.jsx` files. ([#52](https://github.com/reactjs/React.NET/issues/52)) + +Have fun, and as always, please feel free to send feedback or bug reports +[on GitHub](https://github.com/reactjs/React.NET). + +— Daniel From d13d35f4999365df45ebcf0302fd266cd9b5bdfa Mon Sep 17 00:00:00 2001 From: Daniel Lo Nigro Date: Sun, 30 Nov 2014 20:24:55 -0800 Subject: [PATCH 043/859] Bump version number for dev --- build.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.proj b/build.proj index 07f559ad2..81904bae4 100644 --- a/build.proj +++ b/build.proj @@ -11,7 +11,7 @@ of patent rights can be found in the PATENTS file in the same directory. 1 3 - 0 + 1 0 http://reactjs.net/packages/ $(MSBuildProjectDirectory)\tools\MSBuildTasks From 244941baaaebbe75d81fa9afb629d068a6e19646 Mon Sep 17 00:00:00 2001 From: Rick Beerendonk Date: Sat, 10 Jan 2015 15:30:39 +0100 Subject: [PATCH 044/859] Update to React 0.12.2 --- site/jekyll/getting-started/tutorial.md | 8 +- site/jekyll/guides/cassette.md | 2 +- site/jekyll/guides/server-side-rendering.md | 4 +- site/jekyll/index.md | 2 +- .../Views/Home/Index.cshtml | 2 +- src/React.Sample.Mvc4/Views/Home/Index.cshtml | 2 +- src/React/Resources/JSXTransformer.js | 154 +++++++++--------- src/React/Resources/react-with-addons.js | 67 +++++--- 8 files changed, 131 insertions(+), 110 deletions(-) diff --git a/site/jekyll/getting-started/tutorial.md b/site/jekyll/getting-started/tutorial.md index 032ef4d34..a29861e20 100644 --- a/site/jekyll/getting-started/tutorial.md +++ b/site/jekyll/getting-started/tutorial.md @@ -66,7 +66,7 @@ Replace the contents of the new view file with the following:
- + @@ -235,7 +235,7 @@ Markdown is a simple way to format your text inline. For example, surrounding te First, add the third-party **Showdown** library to your application. This is a JavaScript library which takes Markdown text and converts it to raw HTML. We will add it via NuGet (search for "Showdown" and install it, similar to how you installed ReactJS.NET earlier) and reference the script tag in your view: ```html{2} - + ``` @@ -848,7 +848,7 @@ Now that the bundle has been registered, we need to reference it from the view:
- + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() @@ -934,7 +934,7 @@ In the view, we will accept the list of comments as the model, and use `Html.Rea submitUrl = Url.Action("AddComment"), pollInterval = 2000, }) - + @Html.ReactInitJavaScript() diff --git a/site/jekyll/guides/cassette.md b/site/jekyll/guides/cassette.md index 914c6db9e..004362e50 100644 --- a/site/jekyll/guides/cassette.md +++ b/site/jekyll/guides/cassette.md @@ -36,7 +36,7 @@ render from your view using Cassette: ... - + @Bundles.RenderScripts() ``` diff --git a/site/jekyll/guides/server-side-rendering.md b/site/jekyll/guides/server-side-rendering.md index 729f9edcf..551f50657 100644 --- a/site/jekyll/guides/server-side-rendering.md +++ b/site/jekyll/guides/server-side-rendering.md @@ -47,7 +47,7 @@ code. ```html - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() ``` @@ -62,7 +62,7 @@ code. - + ``` diff --git a/site/jekyll/index.md b/site/jekyll/index.md index 7aa903f83..ff3f92248 100644 --- a/site/jekyll/index.md +++ b/site/jekyll/index.md @@ -106,7 +106,7 @@ bundles.Add(new JsxBundle("~/bundles/main").Include( }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() ``` diff --git a/src/React.Sample.Cassette/Views/Home/Index.cshtml b/src/React.Sample.Cassette/Views/Home/Index.cshtml index a6e110d99..b9b91e1c0 100644 --- a/src/React.Sample.Cassette/Views/Home/Index.cshtml +++ b/src/React.Sample.Cassette/Views/Home/Index.cshtml @@ -20,7 +20,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Bundles.RenderScripts() @Html.ReactInitJavaScript() diff --git a/src/React.Sample.Mvc4/Views/Home/Index.cshtml b/src/React.Sample.Mvc4/Views/Home/Index.cshtml index 012d1f293..a3a226843 100644 --- a/src/React.Sample.Mvc4/Views/Home/Index.cshtml +++ b/src/React.Sample.Mvc4/Views/Home/Index.cshtml @@ -17,7 +17,7 @@ @Html.React("CommentsBox", new { initialComments = Model.Comments }) - + @Scripts.Render("~/bundles/main") @Html.ReactInitJavaScript() diff --git a/src/React/Resources/JSXTransformer.js b/src/React/Resources/JSXTransformer.js index 5bc1d333b..b99a0dbbc 100644 --- a/src/React/Resources/JSXTransformer.js +++ b/src/React/Resources/JSXTransformer.js @@ -1,5 +1,5 @@ /** - * JSXTransformer v0.12.1 + * JSXTransformer v0.12.2 */ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.JSXTransformer=e()}}(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>>= @@ -2877,10 +2877,10 @@ parseYieldExpression: true, parseAwaitExpression: true var number = ''; while (index < length) { - if (!isHexDigit(source.charAt(index))) { + if (!isHexDigit(source[index])) { break; } - number += source.charAt(index++); + number += source[index++]; } if (number.length === 0) { @@ -2905,7 +2905,7 @@ parseYieldExpression: true, parseAwaitExpression: true if (isOctalDigit(prefix)) { octal = true; - number = '0' + source.charAt(index++); + number = '0' + source[index++]; } else { octal = false; ++index; @@ -2913,10 +2913,10 @@ parseYieldExpression: true, parseAwaitExpression: true } while (index < length) { - if (!isOctalDigit(source.charAt(index))) { + if (!isOctalDigit(source[index])) { break; } - number += source.charAt(index++); + number += source[index++]; } if (!octal && number.length === 0) { @@ -2941,15 +2941,15 @@ parseYieldExpression: true, parseAwaitExpression: true function scanNumericLiteral() { var number, start, ch, octal; - ch = source.charAt(index); + ch = source[index]; assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), 'Numeric literal must start with a decimal digit or a decimal point'); start = index; number = ''; if (ch !== '.') { - number = source.charAt(index++); - ch = source.charAt(index); + number = source[index++]; + ch = source[index]; // Hex number starts with '0x'. // Octal number starts with '0'. @@ -2965,11 +2965,11 @@ parseYieldExpression: true, parseAwaitExpression: true number = ''; while (index < length) { - ch = source.charAt(index); + ch = source[index]; if (ch !== '0' && ch !== '1') { break; } - number += source.charAt(index++); + number += source[index++]; } if (number.length === 0) { @@ -3001,29 +3001,29 @@ parseYieldExpression: true, parseAwaitExpression: true } while (isDecimalDigit(source.charCodeAt(index))) { - number += source.charAt(index++); + number += source[index++]; } - ch = source.charAt(index); + ch = source[index]; } if (ch === '.') { - number += source.charAt(index++); + number += source[index++]; while (isDecimalDigit(source.charCodeAt(index))) { - number += source.charAt(index++); + number += source[index++]; } - ch = source.charAt(index); + ch = source[index]; } if (ch === 'e' || ch === 'E') { - number += source.charAt(index++); + number += source[index++]; - ch = source.charAt(index); + ch = source[index]; if (ch === '+' || ch === '-') { - number += source.charAt(index++); + number += source[index++]; } if (isDecimalDigit(source.charCodeAt(index))) { while (isDecimalDigit(source.charCodeAt(index))) { - number += source.charAt(index++); + number += source[index++]; } } else { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); @@ -3048,7 +3048,7 @@ parseYieldExpression: true, parseAwaitExpression: true function scanStringLiteral() { var str = '', quote, start, ch, code, unescaped, restore, octal = false; - quote = source.charAt(index); + quote = source[index]; assert((quote === '\'' || quote === '"'), 'String literal must starts with a quote'); @@ -3056,13 +3056,13 @@ parseYieldExpression: true, parseAwaitExpression: true ++index; while (index < length) { - ch = source.charAt(index++); + ch = source[index++]; if (ch === quote) { quote = ''; break; } else if (ch === '\\') { - ch = source.charAt(index++); + ch = source[index++]; if (!ch || !isLineTerminator(ch.charCodeAt(0))) { switch (ch) { case 'n': @@ -3076,7 +3076,7 @@ parseYieldExpression: true, parseAwaitExpression: true break; case 'u': case 'x': - if (source.charAt(index) === '{') { + if (source[index] === '{') { ++index; str += scanUnicodeCodePointEscape(); } else { @@ -3109,16 +3109,16 @@ parseYieldExpression: true, parseAwaitExpression: true octal = true; } - if (index < length && isOctalDigit(source.charAt(index))) { + if (index < length && isOctalDigit(source[index])) { octal = true; - code = code * 8 + '01234567'.indexOf(source.charAt(index++)); + code = code * 8 + '01234567'.indexOf(source[index++]); // 3 digits are only allowed when string starts // with 0, 1, 2, 3 if ('0123'.indexOf(ch) >= 0 && index < length && - isOctalDigit(source.charAt(index))) { - code = code * 8 + '01234567'.indexOf(source.charAt(index++)); + isOctalDigit(source[index])) { + code = code * 8 + '01234567'.indexOf(source[index++]); } } str += String.fromCharCode(code); @@ -3129,7 +3129,7 @@ parseYieldExpression: true, parseAwaitExpression: true } } else { ++lineNumber; - if (ch === '\r' && source.charAt(index) === '\n') { + if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; @@ -3165,20 +3165,20 @@ parseYieldExpression: true, parseAwaitExpression: true ++index; while (index < length) { - ch = source.charAt(index++); + ch = source[index++]; if (ch === '`') { tail = true; terminated = true; break; } else if (ch === '$') { - if (source.charAt(index) === '{') { + if (source[index] === '{') { ++index; terminated = true; break; } cooked += ch; } else if (ch === '\\') { - ch = source.charAt(index++); + ch = source[index++]; if (!isLineTerminator(ch.charCodeAt(0))) { switch (ch) { case 'n': @@ -3192,7 +3192,7 @@ parseYieldExpression: true, parseAwaitExpression: true break; case 'u': case 'x': - if (source.charAt(index) === '{') { + if (source[index] === '{') { ++index; cooked += scanUnicodeCodePointEscape(); } else { @@ -3225,16 +3225,16 @@ parseYieldExpression: true, parseAwaitExpression: true octal = true; } - if (index < length && isOctalDigit(source.charAt(index))) { + if (index < length && isOctalDigit(source[index])) { octal = true; - code = code * 8 + '01234567'.indexOf(source.charAt(index++)); + code = code * 8 + '01234567'.indexOf(source[index++]); // 3 digits are only allowed when string starts // with 0, 1, 2, 3 if ('0123'.indexOf(ch) >= 0 && index < length && - isOctalDigit(source.charAt(index))) { - code = code * 8 + '01234567'.indexOf(source.charAt(index++)); + isOctalDigit(source[index])) { + code = code * 8 + '01234567'.indexOf(source[index++]); } } cooked += String.fromCharCode(code); @@ -3245,14 +3245,14 @@ parseYieldExpression: true, parseAwaitExpression: true } } else { ++lineNumber; - if (ch === '\r' && source.charAt(index) === '\n') { + if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; } } else if (isLineTerminator(ch.charCodeAt(0))) { ++lineNumber; - if (ch === '\r' && source.charAt(index) === '\n') { + if (ch === '\r' && source[index] === '\n') { ++index; } lineStart = index; @@ -3288,7 +3288,7 @@ parseYieldExpression: true, parseAwaitExpression: true startsWith = (option.head) ? '`' : '}'; - if (source.charAt(index) !== startsWith) { + if (source[index] !== startsWith) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } @@ -3306,12 +3306,12 @@ parseYieldExpression: true, parseAwaitExpression: true skipComment(); start = index; - ch = source.charAt(index); + ch = source[index]; assert(ch === '/', 'Regular expression literal must start with a slash'); - str = source.charAt(index++); + str = source[index++]; while (index < length) { - ch = source.charAt(index++); + ch = source[index++]; str += ch; if (classMarker) { if (ch === ']') { @@ -3319,7 +3319,7 @@ parseYieldExpression: true, parseAwaitExpression: true } } else { if (ch === '\\') { - ch = source.charAt(index++); + ch = source[index++]; // ECMA-262 7.8.5 if (isLineTerminator(ch.charCodeAt(0))) { throwError({}, Messages.UnterminatedRegExp); @@ -3345,14 +3345,14 @@ parseYieldExpression: true, parseAwaitExpression: true flags = ''; while (index < length) { - ch = source.charAt(index); + ch = source[index]; if (!isIdentifierPart(ch.charCodeAt(0))) { break; } ++index; if (ch === '\\' && index < length) { - ch = source.charAt(index); + ch = source[index]; if (ch === 'u') { ++index; restore = index; @@ -3360,7 +3360,7 @@ parseYieldExpression: true, parseAwaitExpression: true if (ch) { flags += ch; for (str += '\\u'; restore < index; ++restore) { - str += source.charAt(restore); + str += source[restore]; } } else { index = restore; @@ -8258,10 +8258,10 @@ parseYieldExpression: true, parseAwaitExpression: true lineComment = false; while (index < length) { - ch = source.charAt(index); + ch = source[index]; if (lineComment) { - ch = source.charAt(index++); + ch = source[index++]; if (isLineTerminator(ch.charCodeAt(0))) { loc.end = { line: lineNumber, @@ -8269,7 +8269,7 @@ parseYieldExpression: true, parseAwaitExpression: true }; lineComment = false; addComment('Line', comment, start, index - 1, loc); - if (ch === '\r' && source.charAt(index) === '\n') { + if (ch === '\r' && source[index] === '\n') { ++index; } ++lineNumber; @@ -8292,8 +8292,8 @@ parseYieldExpression: true, parseAwaitExpression: true ++index; comment += '\r'; } - if (ch !== '\r' || source.charAt(index) === '\n') { - comment += source.charAt(index); + if (ch !== '\r' || source[index] === '\n') { + comment += source[index]; ++lineNumber; ++index; lineStart = index; @@ -8302,13 +8302,13 @@ parseYieldExpression: true, parseAwaitExpression: true } } } else { - ch = source.charAt(index++); + ch = source[index++]; if (index >= length) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } comment += ch; if (ch === '*') { - ch = source.charAt(index); + ch = source[index]; if (ch === '/') { comment = comment.substr(0, comment.length - 1); blockComment = false; @@ -8323,7 +8323,7 @@ parseYieldExpression: true, parseAwaitExpression: true } } } else if (ch === '/') { - ch = source.charAt(index + 1); + ch = source[index + 1]; if (ch === '/') { loc = { start: { @@ -8362,7 +8362,7 @@ parseYieldExpression: true, parseAwaitExpression: true ++index; } else if (isLineTerminator(ch.charCodeAt(0))) { ++index; - if (ch === '\r' && source.charAt(index) === '\n') { + if (ch === '\r' && source[index] === '\n') { ++index; } ++lineNumber; @@ -8665,7 +8665,7 @@ parseYieldExpression: true, parseAwaitExpression: true if (!isXJSIdentifierPart(ch)) { break; } - value += source.charAt(index++); + value += source[index++]; } return { @@ -8679,11 +8679,11 @@ parseYieldExpression: true, parseAwaitExpression: true function scanXJSEntity() { var ch, str = '', start = index, count = 0, code; - ch = source.charAt(index); + ch = source[index]; assert(ch === '&', 'Entity must start with an ampersand'); index++; while (index < length && count++ < 10) { - ch = source.charAt(index++); + ch = source[index++]; if (ch === ';') { break; } @@ -8718,7 +8718,7 @@ parseYieldExpression: true, parseAwaitExpression: true var ch, str = '', start; start = index; while (index < length) { - ch = source.charAt(index); + ch = source[index]; if (stopChars.indexOf(ch) !== -1) { break; } @@ -8726,9 +8726,9 @@ parseYieldExpression: true, parseAwaitExpression: true str += scanXJSEntity(); } else { index++; - if (ch === '\r' && source.charAt(index) === '\n') { + if (ch === '\r' && source[index] === '\n') { str += ch; - ch = source.charAt(index); + ch = source[index]; index++; } if (isLineTerminator(ch.charCodeAt(0))) { @@ -8750,7 +8750,7 @@ parseYieldExpression: true, parseAwaitExpression: true function scanXJSStringLiteral() { var innerToken, quote, start; - quote = source.charAt(index); + quote = source[index]; assert((quote === '\'' || quote === '"'), 'String literal must starts with a quote'); @@ -8759,7 +8759,7 @@ parseYieldExpression: true, parseAwaitExpression: true innerToken = scanXJSText([quote]); - if (quote !== source.charAt(index)) { + if (quote !== source[index]) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } @@ -12466,8 +12466,8 @@ function indentBefore(start, state) { var end = start; start = start - 1; - while (start > 0 && state.g.source.charAt(start) != '\n') { - if (!state.g.source.charAt(start).match(/[ \t]/)) { + while (start > 0 && state.g.source[start] != '\n') { + if (!state.g.source[start].match(/[ \t]/)) { end = start; } start--; @@ -12804,7 +12804,7 @@ function renderParams(traverse, node, path, state) { function isParensFreeSingleParam(node, state) { return node.params.length === 1 && - state.g.source.charAt(state.g.position) !== '('; + state.g.source[state.g.position] !== '('; } function renderExpressionBody(traverse, node, path, state) { @@ -14779,7 +14779,7 @@ function addDisplayName(displayName, object, state) { if (safe) { utils.catchup(object['arguments'][0].range[0] + 1, state); - utils.append("displayName: '" + displayName + "',", state); + utils.append('displayName: "' + displayName + '",', state); } } } @@ -15072,7 +15072,7 @@ function renderXJSExpressionContainer(traverse, object, isLast, path, state) { function quoteAttrName(attr) { // Quote invalid JS identifiers. if (!/^[a-z_$][a-z\d_$]*$/i.test(attr)) { - return "'" + attr + "'"; + return '"' + attr + '"'; } return attr; } diff --git a/src/React/Resources/react-with-addons.js b/src/React/Resources/react-with-addons.js index b637678d9..cdba5d224 100644 --- a/src/React/Resources/react-with-addons.js +++ b/src/React/Resources/react-with-addons.js @@ -1,5 +1,5 @@ /** - * React (with addons) v0.12.1 + * React (with addons) v0.12.2 */ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.React=e()}}(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 Date: Sat, 10 Jan 2015 16:13:36 +0100 Subject: [PATCH 045/859] Update copyrights for 2015. --- build.proj | 2 +- site/jekyll/_layouts/default.html | 2 +- src/Cassette.React/AssemblyRegistration.cs | 2 +- src/Cassette.React/Cassette.React.nutrans | 2 +- src/Cassette.React/CassetteMSBuildStartup.cs | 2 +- src/Cassette.React/InsertIntoPipelineJsxBundleProcessor.cs | 2 +- src/Cassette.React/JsxBundleProcessor.cs | 2 +- src/Cassette.React/JsxCompiler.cs | 2 +- src/Cassette.React/JsxFileSearchModifier.cs | 2 +- src/Cassette.React/MSBuildUtils.cs | 2 +- src/Cassette.React/Properties/AssemblyInfo.cs | 2 +- src/Cassette.React/ReactContainerConfiguration.cs | 2 +- .../AssemblyRegistration.cs | 2 +- .../ClearScriptV8InitialisationException.cs | 2 +- src/React.JavaScriptEngine.ClearScriptV8/ClearScriptV8Utils.cs | 2 +- .../Properties/AssemblyInfo.cs | 2 +- .../React.JavaScriptEngine.ClearScriptV8.nutrans | 2 +- src/React.JavaScriptEngine.VroomJs/AssemblyRegistration.cs | 2 +- src/React.JavaScriptEngine.VroomJs/Properties/AssemblyInfo.cs | 2 +- .../React.JavaScriptEngine.VroomJs.nutrans | 2 +- src/React.JavaScriptEngine.VroomJs/VroomJsEngine.cs | 2 +- .../VroomJsInitialisationException.cs | 2 +- src/React.JavaScriptEngine.VroomJs/VroomJsUtils.cs | 2 +- src/React.MSBuild/AssemblyRegistration.cs | 2 +- src/React.MSBuild/MSBuildHost.cs | 2 +- src/React.MSBuild/Properties/AssemblyInfo.cs | 2 +- src/React.MSBuild/React.MSBuild.nutrans | 2 +- src/React.MSBuild/TransformJsx.cs | 2 +- src/React.Sample.Cassette/CassetteConfiguration.cs | 2 +- src/React.Sample.Cassette/Content/Sample.css | 2 +- src/React.Sample.Cassette/Content/Sample.jsx | 2 +- src/React.Sample.Cassette/Global.asax.cs | 2 +- src/React.Sample.Cassette/Properties/AssemblyInfo.cs | 2 +- src/React.Sample.Mvc4/App_Start/BundleConfig.cs | 2 +- src/React.Sample.Mvc4/App_Start/FilterConfig.cs | 2 +- src/React.Sample.Mvc4/App_Start/ReactConfig.cs | 2 +- src/React.Sample.Mvc4/App_Start/RouteConfig.cs | 2 +- src/React.Sample.Mvc4/Content/Sample.css | 2 +- src/React.Sample.Mvc4/Content/Sample.jsx | 2 +- src/React.Sample.Mvc4/Controllers/HomeController.cs | 2 +- src/React.Sample.Mvc4/Global.asax.cs | 2 +- src/React.Tests/Core/FileCacheHashTests.cs | 2 +- src/React.Tests/Core/JavaScriptEngineFactoryTest.cs | 2 +- src/React.Tests/Core/JsxTransformerTests.cs | 2 +- src/React.Tests/Core/ReactComponentTest.cs | 2 +- src/React.Tests/Core/ReactEnvironmentTest.cs | 2 +- src/React.Tests/Mvc/HtmlHelperExtensionsTests.cs | 2 +- src/React.Web.Mvc3/React.Web.Mvc3.nutrans | 2 +- src/React.Web.Mvc4/HtmlHelperExtensions.cs | 2 +- src/React.Web.Mvc4/React.Web.Mvc4.nutrans | 2 +- src/React.Web/AspNetCache.cs | 2 +- src/React.Web/AspNetFileSystem.cs | 2 +- src/React.Web/AssemblyRegistration.cs | 2 +- src/React.Web/Exceptions/ReactAspNetException.cs | 2 +- src/React.Web/IJsxHandler.cs | 2 +- src/React.Web/JsxHandler.cs | 2 +- src/React.Web/JsxHandlerFactory.cs | 2 +- src/React.Web/React.Web.nutrans | 2 +- src/React.Web/TinyIoCAspNetExtensions.cs | 2 +- src/React.Web/WebInitializer.cs | 2 +- src/React/AssemblyRegistration.cs | 2 +- src/React/Exceptions/JsxException.cs | 2 +- src/React/Exceptions/JsxUnsupportedEngineException.cs | 2 +- src/React/Exceptions/ReactConfigurationException.cs | 2 +- src/React/Exceptions/ReactException.cs | 2 +- src/React/Exceptions/ReactInvalidComponentException.cs | 2 +- src/React/Exceptions/ReactScriptLoadException.cs | 2 +- src/React/Exceptions/ReactServerRenderingException.cs | 2 +- src/React/FileCacheHash.cs | 2 +- src/React/FileSystemBase.cs | 2 +- src/React/IAssemblyRegistration.cs | 2 +- src/React/ICache.cs | 2 +- src/React/IFileCacheHash.cs | 2 +- src/React/IFileSystem.cs | 2 +- src/React/IJsxTransformer.cs | 2 +- src/React/IReactComponent.cs | 2 +- src/React/IReactEnvironment.cs | 2 +- src/React/IReactSiteConfiguration.cs | 2 +- src/React/Initializer.cs | 2 +- src/React/JavaScriptEngineExtensions.cs | 2 +- src/React/JavaScriptWithSourceMap.cs | 2 +- src/React/JsxTransformer.cs | 2 +- src/React/NullCache.cs | 2 +- src/React/React.nutrans | 2 +- src/React/ReactComponent.cs | 2 +- src/React/ReactEnvironment.cs | 2 +- src/React/ReactSiteConfiguration.cs | 2 +- src/React/Resources/shims.js | 2 +- src/React/SimpleFileSystem.cs | 2 +- src/React/SourceMap.cs | 2 +- src/React/SystemEnvironmentUtils.cs | 2 +- src/React/TinyIoCExtensions.cs | 2 +- src/SharedAssemblyInfo.cs | 2 +- src/System.Web.Optimization.React/JsxBundle.cs | 2 +- src/System.Web.Optimization.React/JsxTransform.cs | 2 +- src/System.Web.Optimization.React/Properties/AssemblyInfo.cs | 2 +- .../System.Web.Optimization.React.nutrans | 2 +- src/template.nuspec | 2 +- 98 files changed, 98 insertions(+), 98 deletions(-) diff --git a/build.proj b/build.proj index 81904bae4..eecc2fa2c 100644 --- a/build.proj +++ b/build.proj @@ -1,6 +1,6 @@ + \ No newline at end of file diff --git a/src/React.Owin/packages.config b/src/React.Owin/packages.config new file mode 100644 index 000000000..8e9cc26ec --- /dev/null +++ b/src/React.Owin/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/React.Sample.Owin/App.config b/src/React.Sample.Owin/App.config new file mode 100644 index 000000000..a2dd99c6b --- /dev/null +++ b/src/React.Sample.Owin/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/React.Sample.Owin/Content/Index.html b/src/React.Sample.Owin/Content/Index.html new file mode 100644 index 000000000..8d2840113 --- /dev/null +++ b/src/React.Sample.Owin/Content/Index.html @@ -0,0 +1,21 @@ + + + + + + React OWIN Sample + + + + + +
+ + + + \ No newline at end of file diff --git a/src/React.Sample.Owin/Content/Todo.css b/src/React.Sample.Owin/Content/Todo.css new file mode 100644 index 000000000..e2f4d516e --- /dev/null +++ b/src/React.Sample.Owin/Content/Todo.css @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2014-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +body { + font-family: Calibri, Verdana, sans-serif; +} + +ul li { + color: brown +} \ No newline at end of file diff --git a/src/React.Sample.Owin/Content/Todo.jsx b/src/React.Sample.Owin/Content/Todo.jsx new file mode 100644 index 000000000..e3ef2016a --- /dev/null +++ b/src/React.Sample.Owin/Content/Todo.jsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2014-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +var TodoList = React.createClass({ + render: function() { + var createItem = function(itemText) { + return
  • {itemText}
  • ; + }; + return
      {this.props.items.map(createItem)}
    ; + } +}); + +TodoApp = React.createClass({ + getInitialState: function() { + return {items: [], text: ''}; + }, + onChange: function(e) { + this.setState({text: e.target.value}); + }, + handleSubmit: function(e) { + e.preventDefault(); + var nextItems = this.state.items.concat([this.state.text]); + var nextText = ''; + this.setState({items: nextItems, text: nextText}); + }, + render: function() { + return ( +
    +

    TODO

    + +
    + + +
    +
    + ); + } +}); \ No newline at end of file diff --git a/src/React.Sample.Owin/Content/jquery-1.10.0.min.js b/src/React.Sample.Owin/Content/jquery-1.10.0.min.js new file mode 100644 index 000000000..01c688164 --- /dev/null +++ b/src/React.Sample.Owin/Content/jquery-1.10.0.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.0 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.0.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.0",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=lt(),k=lt(),E=lt(),S=!1,A=function(){return 0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=bt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+xt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return At(e.replace(z,"$1"),t,n,i)}function st(e){return K.test(e+"")}function lt(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[b]=!0,e}function ct(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function pt(e,t,n){e=e.split("|");var r,i=e.length,a=n?null:t;while(i--)(r=o.attrHandle[e[i]])&&r!==t||(o.attrHandle[e[i]]=a)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function dt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:t}function gt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function yt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function vt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),r.attributes=ct(function(e){return e.innerHTML="",pt("type|href|height|width",dt,"#"===e.firstChild.getAttribute("href")),pt(B,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),r.input=ct(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),pt("value",ht,r.attributes&&r.input),r.getElementsByTagName=ct(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ct(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ct(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=st(n.querySelectorAll))&&(ct(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ct(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=st(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ct(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=st(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},r.sortDetached=ct(function(e){return 1&e.compareDocumentPosition(n.createElement("div"))}),A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return gt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?gt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:ut,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=bt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?ut(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return at(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:vt(function(){return[0]}),last:vt(function(e,t){return[t-1]}),eq:vt(function(e,t,n){return[0>n?n+t:n]}),even:vt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:vt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:vt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:vt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=mt(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=yt(n);function bt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function wt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function Tt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ct(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function Nt(e,t,n,r,i,o){return r&&!r[b]&&(r=Nt(r)),i&&!i[b]&&(i=Nt(i,o)),ut(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||St(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:Ct(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=Ct(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=Ct(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function kt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=wt(function(e){return e===t},s,!0),p=wt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[wt(Tt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return Nt(l>1&&Tt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),i>r&&kt(e=e.slice(r)),i>r&&xt(e))}f.push(n)}return Tt(f)}function Et(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=Ct(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?ut(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=bt(e)),n=t.length;while(n--)o=kt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Et(i,r))}return o};function St(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function At(e,t,n,i){var a,s,u,c,p,f=bt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&xt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}o.pseudos.nth=o.pseudos.eq;function jt(){}jt.prototype=o.filters=o.pseudos,o.setFilters=new jt,r.sortStable=b.split("").sort(A).join("")===b,p(),[0,0].sort(A),r.detectDuplicates=S,x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!u||(n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s; +if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=x(this),l=t,u=e.match(T)||[];while(o=u[a++])l=r?l:!s.hasClass(o),s[l?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle);u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}) +}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("