Over the past 2 months we've been taking feedback and working hard to make React even better. We've also added several features that awe didn't finish in time for the v0.3 launch, and are proud to announce the availability of React v0.4 today!
+
+
This release could not have happened without the support of our growing community. Since launch day, the community has contributed blog posts, questions to the Google Group, and issues and pull requests on GitHub. We've had contributions ranging from documentation improvements to major changes to React's rendering. We've seen people integrate React into the tools they're using and the products they're building, and we're all very excited to see what our budding community builds next!
+
+
React v0.4 has some big changes. We've also restructured the documentation to better communicate how to use React. We've summarized the changes below and linked to documentation where we think it will be especially useful.
Switch from using id attribute to data-reactid to track DOM nodes. This allows you to integrate with other JS and CSS libraries more easily.
+
Support for more DOM elements and attributes (e.g., <canvas>)
+
Improved server-side rendering APIs. React.renderComponentToString(<component>, callback) allows you to use React on the server and generate markup which can be sent down to the browser.
Improvements to forms. We've written wrappers around <input>, <textarea>, <option>, and <select> in order to standardize many inconsistencies in browser implementations. This includes support for defaultValue, and improved implementation of the onChange event, and circuit completion. Read the docs for details...
+
We've implemented an improved synthetic event system that conforms to the W3C spec.
+
Updates to your component are batched now, which may result in a significantly faster re-render of components. this.setState now takes an optional callback as it's second parameter. If you were using onClick={this.setState.bind(this, state)} previously, you'll want to make sure you add a third parameter so that the event is not treated as the callback.
+
+
+
JSX
+
+
+
Support for comment nodes <div>{/* this is a comment and won't be rendered */}</div>
+
Children are now transformed directly into arguments instead of being wrapped in an array
+E.g. <div><Component1/><Component2></div> is transformed into React.DOM.div(null, Component1(null), Component2(null)).
+Previously this would be transformed into React.DOM.div(null, [Component1(null), Component2(null)]).
+If you were using React without JSX previously, your code should still work.
+
+
+
react-tools
+
+
+
Fixed a number of bugs when transforming directories
+
No longer re-write require()s to be relative unless specified
Over the past 2 months we've been taking feedback and working hard to make React even better. We've also added several features that awe didn't finish in time for the v0.3 launch, and are proud to announce the availability of React v0.4 today!
Flow of docs should mimic progression of questions a new user would ask
+
High information density -- assume the reader is adept at JS
+
Talk about best practices
+
JSFiddles for all code samples
+
Provide background for some of the design decisions
+
Less words less words less words!
+
+
+
Outline
+
+
Motivation / Why React?
+- Declarative (simple)
+- Components (separation of concerns)
+- Give it 5 minutes
+
+
Displaying data
+- Hello world example
+- Reactive updates
+- Components are just functions
+- JSX syntax (link to separate doc?)
+- JSX gotchas
+
+
Interactivity and dynamic UIs
+- Click handler example
+- Event handlers / synthetic events (link to w3c docs)
+- Under the hood: autoBind and event delegation (IE8 notes)
+- React is a state machine
+- How state works
+- What components should have state?
+- What should go in state?
+- What shouldn't go in state?
+
+
Scaling up: using multiple components
+- Motivation: separate concerns
+- Composition example
+- Ownership (and owner vs. parent)
+- Children
+- Data flow (one-way data binding)
+- A note on performance
+
+
Building effective reusable components
+- You should build a reusable component library (CSS, testing etc)
+- Prop validation
+- Transferring props: a shortcut
+- Mixins
+- Testing
+
+
Forms
+
+
Working with the browser
+- The mock DOM
+- Refs / getDOMNode()
+- More about refs
+- Component lifecycle
+- Browser support and polyfills
+
+
Working with your environment
+- CDN-hosted React
+- Using master
+- In-browser JSX transform
+- Productionizing: precompiled JSX
+- Helpful open-source projects
+
+
Integrating with other UI libraries
+- Using jQuery plugins
+- Letting jQuery manage React components
+- Using with Backbone.View
+- CoffeeScript
+- Moving from Handlebars to React: an example
+
+
Server / static rendering
+- Motivation
+- Simple example
+- How does it work? (No DOM)
+- Rendr + React
+
+
Big ideas
+- Animation
+- Bootstrap bindings (responsive grids)
+- Reactive CSS
+- Web workers
+- Native views
+
+
Case studies
+- Comment box tutorial from scratch
+- From HTML mock to application: React one-hour email
+- Jordan's LikeToggler example
Composite components extend a ReactCompositeComponent base class that provides
-a very powerful API that makes React flexible and able to easily work with other
-libraries and frameworks.
-
-
Lifecycle Methods
-
-
Composite components can optionally implement lifecycle methods that are invoked
-at various stages in the component lifecycle that
-each have unique guarantees.
-
-
Mounting
-
-
-
getInitialState(): object is invoked before a component is mounted.
-Stateful components should implement this and return the initial state data.
-
componentWillMount() is invoked immediately before mounting occurs.
-
componentDidMount(DOMElement rootNode) is invoked immediately after
-mounting occurs. Initialization that requires DOM nodes should go here.
-
-
-
Updating
-
-
-
componentWillReceiveProps(object nextProps) is invoked when a mounted
-component receives new props. This method should be used to compare
-this.props and nextProps to perform state transitions using
-this.setState().
-
shouldComponentUpdate(object nextProps, object nextState): boolean is
-invoked when a component decides whether any changes warrant an update to the
-DOM. Implement this as an optimization to compare this.props with
-nextProps and this.state with nextState and return false if React
-should skip updating.
-
componentWillUpdate(object nextProps, object nextState) is invoked
-immediately before updating occurs. You cannot call this.setState() here.
componentWillUnmount() is invoked immediately before a component is
-unmounted and destroyed. Cleanup should go here.
-
-
-
Mounted Methods
-
-
Mounted composite components also support the following methods:
-
-
-
getDOMNode(): DOMElement can be invoked on any mounted component in order
-to obtain a reference to its rendered DOM node.
-
forceUpdate() can be invoked on any mounted component when you know that
-some deeper aspect of the component's state has changed without using
-this.setState().
-
-
-
-
Note:
-
-
The DOMElement rootNode argument of componentDidMount() and
-componentDidUpdate() is a convenience. The same node can be obtained by
-calling this.getDOMNode().
-
-
-
Component Refs
-
-
A common use case of event callbacks or the lifecycle methods is to operate on a
-component returned by render(). For example, consider a search component that
-should auto-focus the input once mounted:
Although this implementation works, it is fragile because componentDidMount()
-now relies on render() returning a particular DOM structure.
-
-
React provides a better way for composite components to reference components
-that it constructs in its render() method through the use of refs. A component
-can assign a ref to any component it constructs. This will create a reference
-to those components on this.refs. For example:
In this example, this.refs.searchInput will reference the <input> component
-and is available in most lifecycle methods and event callbacks. We obtain a
-reference to the <input>'s DOM node using getDOMNode().
-
-
-
Note:
-
-
If you want to preserve compatibility with Google Closure Compiler's
-property crushing in ADVANCED_OPTIMIZATIONS mode, make sure to use string
-literals with this.refs.
React supports the latest two Chrome, Firefox, Safari, and Internet Explorer versions. React can work with Internet Explorer 8 with polyfills.
-
-
How do I get React to support Internet Explorer 8?
-
-
React requires ES5 JavaScript shims to run in Internet Explorer 8. Include the ES5 Shims to implement these shims.
-
-
Who uses React?
-
-
The Instagram website is built entirely in React. The Facebook website is also increasingly using React, including the common commenting plugin across the site.
Components are the basic units of composition in React. Components encapsulate
-the logic necessary to take input parameters and render markup. Components can
-be rendered into an existing DOM element on the page by using
-React.renderComponent:
-
// Replaces everything in `document.body` with <div>Hello, world!</div>;
-React.renderComponent(<div>Hello,world!</div>, document.body);
-
-
Keep in mind that <div> is not a DOM element! Keep reading...
-
-
Types of Components
-
-
There are two types of components:
-
-
-
Composite Components
-
DOM Components
-
-
-
Composite Components such as TodoApp and Typeahead.
-
-
The majority of your React code will be implementing composite components.
-
-
Composite components are higher-level components with custom rendering logic
-that may compose other composite components or DOM components.
This example defines a LinkButton component class using React.createClass(),
-and its render() method composes the <a> DOM component.
-
-
DOM Components such as div and span.
-
-
DOM components are the set of classes that correspond to browser DOM elements.
-They are defined in React.DOM and can be brought "into scope" by setting
-@jsx React.DOM in the docblock. See JSX Syntax for more
-details.
-
-
Although React.DOM components look like browser DOM elements, they differ in a
-few ways:
-
-
-
All property names, including event handlers, are camelCased.
-
JavaScript identifiers should be used, namely className and htmlFor.
-
The style prop expects an object instead of a string. The object should map
-camelCased style properties to values, e.g. {backgroundColor: '#fff'}.
-
-
-
Here is an example of a React link styled as a button with a click handler:
In this example, LikeLink takes liked as boolean data. This type of data
-that is passed in is called a "prop". Examples of props on DOM components
-include className and onClick.
-
-
Whenever a component's props change, its render() function will be
-re-evaluated and the DOM will be updated. React will ensure that the DOM is
-always kept up-to-date.
-
-
State
-
-
Let's build a small LikeApp application that makes use of the <LikeLink>
-component from above. It should start off unliked and we should be able to like
-it by clicking the link:
This renders a <LikeLink> with a click listener. However, it is not clear how
-handleClick should update isClicked to true. LikeApp needs a way to store
-state about whether or not it has been clicked.
-
-
State vs. Props
-
-
State is data that is managed internally by a composite component. Like props,
-the render() function will be re-evaluated whenever state changes. Props and
-state differ in that:
There's a lot going on here, so let's work our way from top to bottom:
-
-
-
getInitialState() describes what state data looks like when the component
-is created.
-
In render(), state data can be accessed via this.state.
-
When the link is clicked, we update state using setState().
-
-
-
Now when we click the link, the <LikeLink> will get updated, right? Wrong.
-
-
Transferring Props
-
-
If you have been following carefully, you may have noticed that although we pass
-a click handler into <LikeLink> as a prop, LikeLink does not do anything
-with this.props.onClick! Let's fix that.
Although this works, realize that this would quickly become tedious if we wanted
-to also transfer href, title, target, and other events from this to the
-rendered <a>. React provides a convenience method, transferPropsTo(), for
-transferring props:
This will transfer all props from this to the specified component (including
-onClick).
-
-
Summary
-
-
Now we are done. LikeApp renders an unliked link which, when clicked, will:
-
-
-
Update the internal state of LikeApp.
-
Change the props passed into LikeLink.
-
Change the return value of render().
-
Trigger an update to the DOM.
-
-
-
It's worth noting that React will handle new return values of render() by
-making the minimal set of mutations necessary to bring the DOM up-to-date. In
-this case, only the textContent of the rendered link will be mutated.
-
-
In summary:
-
-
-
Props are passed in whereas state is managed internally by a component.
-
Never mutate this.props or this.state. You should pass props into other
-components and mutate state using setState().
-
State is private. Never read state or call setState() on
-anything but this.
-
Whenever props or state changes, render() will be re-evaluated and the DOM
-updated. Also, render() should not depend on anything besides this.props
-and this.state.
The second call to React.renderComponent() will trigger the following:
-
-
-
The <div> component will check the new props to see if anything changed.
-
The set of changes are used to update the DOM node as necessary.
-
-
-
Updating is the process of mutating the rendered DOM nodes and occurs
-whenever either props or state has changed. This ensures that the rendered DOM
-is consistent with the data.
-
-
Unmounting
-
-
Let's add one final call to React.renderComponent() after another three
-seconds:
The third call to React.renderComponent() will trigger the following:
-
-
-
An <img> (defined by React.DOM.img) component is instantiated.
-
React will compare the <div> component with the <img> component.
-
Since the component class is different, the <div> component will be
-unmounted.
-
The <img> component will then be mounted into document.body.
-
-
-
Unmounting is the process of releasing resources that have been allocated by
-a component. This allows user interfaces built with React to live long without
-memory leaks.
-
-
Components can also be unmounted using
-React.unmountAndReleaseReactRootNode():
The most basic thing you can do with a UI is display some data. React makes it easy to display data and automatically keeps the interface up-to-date when the data changes.
+
+
Getting Started
+
+
Let's look at a really simple example. Create a hello-react.html file with the following code:
For the rest of the documentation, we'll just focus on the JavaScript code and assume it's inserted into a template like the one above. Replace the placeholder comment above with the following JS:
Open hello-react.html in a web browser and type your name into the text field. Notice that React is only changing the time string in the UI — any input you put in the text field remains, even though you haven't written any code to manage this behavior. React figures it out for you and does the right thing.
+
+
The way we are able to figure this out is that React does not manipulate the DOM unless it needs to. It uses a fast, internal mock DOM to perform diffs and computes the most efficient DOM mutation for you.
+
+
The inputs to this component are called props — short for "properties". They're passed as attributes in JSX syntax. You should think of these as immutable within the component, that is, never write to this.props.
+
+
Components are Just Like Functions
+
+
React components are very simple. You can think of them as simple function that take in props and state (discussed later) and render HTML. Because they're so simple, it makes them very easy to reason about.
+
+
+
Note:
+
+
One limitation: React components can only render a single root node. If you want to return multiple nodes they must be wrapped in a single root.
+
+
+
JSX Syntax
+
+
We strongly believe that components are the right way to separate concerns rather than "templates" and "display logic." We think that markup and the code that generates it are intimately tied together. Additionally, display logic is often very complex and using template languages to express it becomes cumbersome.
+
+
We've found that the best solution for this problem is to generate markup directly from the JavaScript code such that you can use all of the expressive power of a real programming language to build UIs. In order to make this easier, we've added a very simple, optional HTML-like syntax for the function calls that generate markup called JSX.
+
+
JSX lets you write JavaScript function calls with HTML syntax. To generate a link in React using pure JavaScript you'd write: React.DOM.a({href: 'http://facebook.github.io/react/'}, 'Hello React!'). With JSX this becomes <a href="http://facebook.github.io/react/">Hello React!</a>. We've found this has made building React apps easier and designers tend to prefer the syntax, but everyone has their own workflow, so JSX is not required to use React.
+
+
JSX is very small; the "hello, world" example above uses every feature of JSX. To learn more about it, see JSX in depth. Or see the transform in action in our live JSX compiler.
+
+
JSX is similar to HTML, but not exactly the same. See JSX gotchas for some key differences.
+
+
The easiest way to get started with JSX is to use the in-browser JSXTransformer. We strongly recommend that you don't use this in production. You can precompile your code using our command-line react-tools package.
When <Clicker> is clicked, the handleClick() function will get fired. Under
-the hood, React uses top-level event delegation to achieve high performance.
-
-
Automatically Binding Callbacks
-
-
Just like any callback in JavaScript, if you want to refer to the component as
-this from the callback, you need to bind the callback to the component:
Binding a function allocates memory to create a new bound function. Since
-render() may be invoked many times, it is a bad place to bind functions.
-React.autoBind() sidesteps this issue by only binding once at instantiation
-time.
-
-
-
DOM Events
-
-
React uses top-level event delegation
-to achieve high performance when implementing DOM events. For each type of DOM
-event, React adds a single top-level listener and determines which event
-handlers to execute by simulating event capturing and bubbling.
-
-
DOM event handlers are called with a normalized AbstractEvent object that has
-cross-browser compatible implementations of stopPropagation and
-preventDefault(). If you need access to the raw browser event, you can use the
-nativeEvent property.
-
-
-
Note:
-
-
The AbstractEvent object is JSON serializable so that React applications can
-be executed inside web workers.
-
-
-
Touch Events
-
-
If you want to use touch events, you must configure React's event system to
-initialize them:
-
// Invoke before calling `React.renderComponent()`.
-React.initializeTouchEvents(true);
-
-
Custom Events
-
-
Notice that event listeners are attached by simply passing them into components
-as props. For DOM components, events are handled using top-level event
-delegation. For composite components, event handling is up to the component's
-implementation.
-
-
Here is an example of a toggle link that fires a custom onToggle event:
With React your event handlers should be quite small. Large event handlers may
-be symptomatic of code that should be moved into helpers or into render().
-Here are some common usage patterns for event handlers.
-
-
Updating State
-
-
The most common thing to do in response to a user action is to call
-this.setState() to update the component's state, which will in turn trigger
-an update to the rendered component.
-
-
Server Requests
-
-
Many event handlers will issue a server request to read or write some data in
-response to an event. The response handler for the request will often call
-this.setState().
-
-
Invoke a Callback
-
-
Your component will often be a small, reusable building block that does not know
-how to respond to a user action. In these situations, we delegate the
-responsibility to the owner by exposing a handler on this.props. This is what
-the ToggleLink example above is doing.
-
-
Inter-component Communication
-
-
A common scenario involves communicating to Component A that a user action
-has occurred on Component B. To solve this problem, a common parent to
-both components should listen for the event on Component B, update its
-internal state, and pass that data into Component A.
-
-
For example, say we have two components: Clicker, a component that fires an
-onCountChange custom event, and ClickCountLabel, a component that displays
-the number of clicks that have happened:
In order to communicate the click count from Clicker to ClickCountLabel, we
-modify ClickApp to maintain state that will be passed into ClickCountLabel:
Form components such as <input>, <textarea>, and <option> differ from other native components because they can be mutated via user interactions. These components provide interfaces that make it easier to manage forms in response to user interactions.
+
+
Interactive Props
+
+
Form components support a few props that are affected via user interactions:
+
+
+
value, supported by <input> and <textarea> components.
+
checked, supported by <input> components of type checkbox or radio.
+
selected, supported by <option> components.
+
+
+
In HTML, the value of <textarea> is set via children. In React, you should use value instead.
+
+
Form components allow listening for changes by setting a callback to the onChange prop. The onChange prop works across browsers to fire in response to user interactions when:
+
+
+
The value of <input> or <textarea> changes.
+
The checked state of <input> changes.
+
The selected state of <option> changes.
+
+
+
Like all DOM events, the onChange prop is supported on all native components and can be used to listen to bubbled change events.
+
+
Controlled Components
+
+
An <input> with value set is a controlled component. In a controlled <input>, the value of the rendered element will always reflect the value prop. For example:
This will render an input that always has a value of Hello!. Any user input will have no effect on the rendered element because React has declared the value to be Hello!. If you wanted to update the value in response to user input, you could use the onChange event:
In this example, we are simply accepting the newest value provided by the user and updating the value prop of the <input> component. This pattern makes it easy to implement interfaces that respond to or validate user interactions. For example:
This would accept user input but truncate the value to the first 140 characters.
+
+
Uncontrolled Components
+
+
An <input> that does not supply a value (or sets it to null) is an uncontrolled component. In an uncontrolled <input>, the value of the rendered element will reflect the user's input. For example:
This will render an input that starts off with an empty value. Any user input will be immediately reflected by the rendered element. If you wanted to listen to updates to the value, you could use the onChange event just like you can with controlled components.
+
+
If you want to initialize the component with a non-empty value, you can supply a defaultValue prop. For example:
This example will function much like the Controlled Components example above.
+
+
Likewise, <input> supports defaultChecked and <option> supports defaultSelected.
+
+
Advanced Topics
+
+
Why Controlled Components?
+
+
Using form components such as <input> in React presents a challenge that is absent when writing traditional form HTML. For example, in HTML:
+
<inputtype="text"name="title"value="Untitled"/>
+
+
This renders an input initialized with the value, Untitled. When the user updates the input, the node's value property will change. However, node.getAttribute('value') will still return the value used at initialization time, Untitled.
+
+
Unlike HTML, React components must represent the state of the view at any point in time and not only at initialization time. For example, in React:
Since this method describes the view at any point in time, the value of the text input should always be Untitled.
+
+
Why Textarea Value?
+
+
In HTML, the value of <textarea> is usually set using its children:
+
<!-- counterexample: DO NOT DO THIS! -->
+ <textareaname="description">This is the description.</textarea>
+
+
For HTML, this easily allows developers to supply multiline values. However, since React is JavaScript, we do not have string limitations and can use \n if we want newlines. In a world where we have value and defaultValue, it is ambiguous what role children play. For this reason, you should not use children when setting <textarea> values:
+
<textareaname="description"value="This is a description."/>
+
+
If you do decide to use children, they will behave like defaultValue.
With React you simply pass your event handler as a camelCased prop similar to how you'd do it in normal HTML. React ensures that all events behave identically in IE8 and above by implementing a synthetic event system. That is, React knows how to bubble and capture events according to the spec, and the events passed to your event handler are guaranteed to be consistent with the W3C spec, regardless of which browser you're using.
+
+
If you'd like to use React on a touch device (i.e. a phone or tablet), simply call React.initializeTouchEvents(true); to turn them on.
+
+
Under the Hood: autoBind and Event Delegation
+
+
Under the hood React does a few things to keep your code performant and easy to understand.
+
+
Autobinding: When creating callbacks in JavaScript you usually need to explicitly bind a method to its instance such that the value of this is correct. With React, every method is automatically bound to its component instance. React caches the bound method such that it's extremely CPU and memory efficient. It's also less typing!
+
+
Event delegation: React doesn't actually attach event handlers to the nodes themselves. When React starts up, it starts listening for all events at the top level using a single event listener. When a component is mounted or unmounted, the event handlers are simply added or removed from an internal mapping. When an event occurs, React knows how to dispatch it using this mapping. When there are no event handlers left in the mapping, React's event handlers are simple no-ops. To learn more about why this is fast, see David Walsh's excellent blog post.
+
+
Components are Just State Machines
+
+
React thinks of UIs as simple state machines. By thinking of a UI as being in various states and rendering those states, it's easy to keep your UI consistent.
+
+
In React, you simply update a component's state, and then render a new UI based on this new state. React takes care of updating the DOM for you in the most efficient way.
+
+
How State Works
+
+
A common way to inform React of a data change is by calling setState(data, callback). This method merges data into this.state and re-renders the component. When the component finishes re-rendering, the optional callback is called. Most of the time you'll never need to provide a callback since React will take care of keeping your UI up-to-date for you.
+
+
What Components Should Have State?
+
+
Most of your components should simply take some data from props and render it. However, sometimes you need to respond to user input, a server request or the passage of time. For this you use state.
+
+
Try to keep as many of your components as possible stateless. By doing this you'll isolate the state to its most logical place and minimize redundancy, making it easier to reason about your application.
+
+
A common pattern is to create several stateless components that just render data, and have a stateful component above them in the hierarchy that passes its state to its children via props. The stateful component encapsulates all of the interaction logic, while the stateless components take care of rendering data in a declarative way.
+
+
What Should Go in State?
+
+
State should contain data that a component's event handlers may change to trigger a UI update. In real apps this data tends to be very small and JSON-serializable. When building a stateful component, think about the minimal possible representation of its state, and only store those properties in this.state. Inside of render() simply compute any other information you need based on this state. You'll find that thinking about and writing applications in this way tends to lead to the most correct application, since adding redundant or computed values to state means that you need to explicitly keep them in sync rather than rely on React computing them for you.
+
+
What Shouldn't Go in State?
+
+
this.state should only contain the minimal amount of data needed to represent your UI's state. As such, it should not contain:
+
+
+
Computed data: Don't worry about precomputing values based on state — it's easier to ensure that your UI is consistent if you do all computation within render(). For example, if you have an array of list items in state and you want to render the count as a string, simply render this.state.listItems.length + ' list items' in your render() method rather than storing it on state.
+
React components: Build them in render() based on underlying props and state.
+
Duplicated data from props: Try to use props as the source of truth where possible. Because props can change over time, it's appropriate to store props in state to be able to know its previous values.
JSX looks like HTML but there are some important differences you may run into.
-
Whitespace removal
+
Whitespace Removal
JSX doesn't follow the same whitespace elimination rules as HTML. JSX removes all whitespace between two curly braces expressions. If you want to have whitespace, simply add {' '}.
JSX supports both single-line and multi-line JavaScript comments within a tag declaration:
-
<div// This is a single-line comment:
- /*
- And a multi-line
- comment
- */
-/>
-
-
As of React 0.3, there is no good way to insert comments within the children section. Issue #82 is tracking progress to enable the following:
-
// Note: This is not implemented yet!
-<div>
- {/* This is a comment */}
-</div>
-
Custom HTML Attributes
If you pass properties to native HTML elements that do not exist in the HTML specification, React will not render them. If you want to use a custom attribute, you should prefix it with data-.
JSX is a JavaScript XML syntax transform recommended (but not required) for use
+
JSX in Depth
+
+
JSX is a JavaScript XML syntax transform recommended for use
with React.
Why JSX?
-
First of all, don't use JSX if you don't like it!
-
React works out of the box without JSX. Simply construct your markup using the
functions on React.DOM. For example, here's how to construct a simple link:
varlink=React.DOM.a({href:'http://facebook.github.io/react'},'React');
@@ -109,13 +196,13 @@ functions on React.DOM. For example, here's how to construct a
The Transform
-
JSX transforms XML-like syntax into native JavaScript. It turns XML elements and
-attributes into function calls and objects, respectively.
+
JSX transforms from an XML-like syntax into native JavaScript. XML elements and
+attributes are transformed into function calls and objects, respectively.
To construct an instance of a composite component, create a variable that
references the class.
@@ -186,7 +273,7 @@ will use that field to find DOM components.
// Input (JSX):vartree=<Nav><span/></Nav>;// Output (JS):
-vartree=Nav(null,React.DOM.span(null,null));
+vartree=Nav(null,React.DOM.span(null));
Remember:
@@ -198,7 +285,7 @@ used elements. In general, JSX has no notion of the DOM.
JavaScript Expressions
-
Attribute Expressions
+
Attribute Expressions
To use a JavaScript expression as an attribute value, wrap the expression in a
pair of curly braces ({}) instead of quotes ("").
@@ -207,13 +294,18 @@ pair of curly braces ({}) instead of quotes (""// Output (JS):
varperson=Person({name:window.isLoggedIn?window.name:''});
-
Child Expressions
+
Child Expressions
Likewise, JavaScript expressions may be used to express children:
Mixins allow code to be shared between multiple React components. They are pretty similar to mixins
-in Python or traits in PHP. Let's look at a simple example:
A class can use multiple mixins, but no two mixins can define the same method. Two mixins can, however,
-implement the same lifecycle method. In this case, each implementation will be invoked one after another.
-
-
The only exception is the shouldComponentUpdate lifecycle method. This method may only be implemented once
-(either by a mixin or by the component).
In general, add a mixin whenever you want a component to share some utility methods, public interface,
-or lifecycle behavior. Often it's appropriate to use them as you would use a superclass in another OOP language.
After returning the structure of your UI from the render method, you may find yourself wanting to "reach out" and invoke methods on component instances returned from render. Often, doing something like this isn't necessary for making data flow through your application, because the Reactive data flow always ensures that the most recent props are sent to each child that is output from render(). However there are a few cases, where it still might be necessary or beneficial.
+
+
Consider the case when you wish to tell an <input /> element (that exists within your instances sub-hierarchy) to focus after you update its value to be the empty string, ''.
Notice how, in this example, we want to "tell" the input something - something that it cannot infer from it's props over time. In this case we want to "tell" it that it should now become focused. However, there are some challenges. What is returned from render()` is not your actual composition of "child" components, it is merely a description of the children at a particular instance in time - a snapshot, if you will.
+
+
+
Note:
+
+
Remember, what you return from render() is not your actual rendered children instances. What you return from render() is merely a description of the children instances in your component's sub-hierarchy at a particular moment in time.
+
+
+
This means that you should never "hold onto" something that you return from render() and then expect it to be anything meaningful.
+
// counterexample: DO NOT DO THIS!
+ render:function(){
+ varmyInput=<input/>;// I'm going to try to call methods on this
+ this.rememberThisInput=myInput;// input at some point in the future! YAY!
+ return(
+ <div>
+ <div>...</div>
+ {myInput}
+ </div>
+ );
+ }
+
+
In this counterexample, the <input /> is merely a description of an <input />. This description is used to create a realbacking instance for the <input />.
+
+
So how do we talk to the real backing instance of the input?
+
+
The ref Attribute
+
+
React supports a very special property that you can attach to any component that is output from render(). This special property allows you to refer to the corresponding backing instance of anything returned from render(). It is always guaranteed to be the proper instance, at any point in time.
+
+
It's as simple as:
+
+
1. Assign a ref attribute to anything returned from render such as:
+
<inputref="myInput"/>
+
+
2. In some other code (typically event handler code), access the backing instance via this.refs as in:
In this example, our render function returns a description of an <input /> instance. But the true instance is accessed via this.refs.theInput. As long as a child component with ref="theInput" is returned from render, this.refs.theInput will access the the proper instance. This even works on higher level (non-DOM) components such as <Typeahead ref="myTypeahead" />.
+
+
Summary
+
+
Refs are a great way to send a message to a particular child instance in a way that would be inconvenient to do via streaming Reactive props and state. They should, however, not be your go-to abstraction for flowing data through your application. By default, use the Reactive data flow and save refs for use cases that are inherently non-reactive.
+
+
Benefits:
+
+
+
You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as this.refs.myTypeahead.reset()).
+
Performing DOM measurements almost always requires reaching out to a "native" component such as <input /> and accessing its underlying DOM node via this.refs.myInput.getDOMNode(). Refs are one of the only practical ways of doing this reliably.
+
Refs are automatically book-kept for you! If that child is destroyed, its ref is also destroyed for you. No worrying about memory here (unless you do something crazy to retain a reference yourself).
+
+
+
Cautions:
+
+
+
Never access refs inside of any component's render method - or while any component's render method is even running anywhere in the call stack.
+
If you want to preserve Google Closure Compiler Crushing resilience, make sure to never access as a property what was specified as a string. This means you must access using this.refs['myRefString'] if your ref was defined as ref="myRefString".
+
If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use refs to "make things happen" - instead, the data flow will usually accomplish your goal.
So far, we've looked at how to write a single component to display data and handle user input. Next let's examine one of React's finest features: composability.
+
+
Motivation: Separation of Concerns
+
+
By building modular components that reuse other components with well-defined interfaces, you get much of the same benefits that you get by using functions or classes. Specifically you can separate the different concerns of your app however you please simply by building new components. By building a custom component library for your application, you are expressing your UI in a way that best fits your domain.
+
+
Composition Example
+
+
Let's create a simple Avatar component which shows a profile picture and username using the Facebook Graph API.
In the above example, instances of Avatarown instances of ProfilePic and ProfileLink. In React, an owner is the component that sets the props of other components. More formally, if a component X is created in component Y's render() method, it is said that X is owned byY. As discussed earlier, a component cannot mutate its props — they are always consistent with what its owner sets them to. This key property leads to UIs that are guaranteed to be consistent.
+
+
It's important to draw a distinciton between the owner-ownee relationship and the parent-child relationship. The owner-ownee relationship is specific to React, while the parent-child relationship is simply the one you know and love from the DOM. In the example above, Avatar owns the div, ProfilePic and ProfileLink instances, and div is the parent (but not owner) of the ProfilePic and ProfileLink instances.
+
+
Children
+
+
When you create a React component instance, you can include additional React components or JavaScript expressions between the opening and closing tags like this:
+
<Parent><Child/></Parent>
+
+
Parent can read its children by accessing the special this.props.children prop.
+
+
Child Reconciliation
+
+
Reconciliation is the process by which React updates the DOM with each new render pass. In general, children are reconciled according to the order in which they are rendered. For example, suppose two render passes generate the following respective markup:
Intuitively, <p>Paragraph 1</p> was removed. Instead, React will reconcile the DOM by changing the text content of the first child and destroying the last child. React reconciles according to the order of the children.
+
+
Stateful Children
+
+
For most components, this is not a big deal. However, for stateful components that maintain data in this.state across render passes, this can be very problematic.
+
+
In most cases, this can be sidestepped by hiding elements instead of destroying them:
The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a key:
When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).
+
+
Data Flow
+
+
In React, data flows from owner to owned component through props as discussed above. This is effectively one-way data binding: owners bind their owned component's props to some value the owner has computed based on its props or state. Since this process happens recursively, data changes are automatically reflected everywhere they are used.
+
+
A Note on Performance
+
+
You may be thinking that it's expensive to react to changing data if there are a large number of nodes under an owner. The good news is that JavaScript is fast and render() methods tend to be quite simple, so in most applications this is extremely fast. Additionally, the bottleneck is almost always the DOM mutation and not JS execution and React will optimize this for you using batching and change detection.
+
+
However, sometimes you really want to have fine-grained control over your performance. In that case, simply override shouldComponentUpdate() to return false when you want React to skip processing of a subtree. See the React reference docs for more information.
+
+
+
Note:
+
+
If shouldComponentUpdate() returns false when data has actually changed, React can't keep your UI in sync. Be sure you know what you're doing while using it, and only use this function when you have a noticeable performance problem. Don't underestimate how fast JavaScript is relative to the DOM.
React is the entry point to the React framework. If you're using one of the prebuilt packages it's available as a global; if you're using CommonJS modules you can require() it.
@@ -101,41 +212,35 @@
Configure React's event system to handle touch events on mobile devices.
-
React.autoBind
-
functionautoBind(functionmethod)
-
-
Marks the provided function to be automatically bound to each React component instance created. This allows React components to define automatically bound methods and ensure that when called they will always reference their current instance.
Creates a component given a specification. A component implements a render method which returns one single child. That child may have an arbitrarily deep child structure. One thing that makes components different than a standard prototypal classes is that you don't need to call new on them. They are convenience wrappers that construct backing instances (via new) for you.
Renders a React component into the DOM in the supplied container.
If the React component was previously rendered into container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React component.
Render a component to its initial HTML. This should only be used on the server. React will call callback with an HTML string when the markup is ready. You can use this method to create static site generators, or you can generate HTML on the server and send it down to have a very fast initial page load. If you call React.renderComponent() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
+
+
AbstractEvent
Your event handlers will be passed instances of AbstractEvent, a cross-browser wrapper around the browser's native event. It has the same interface as the browser's native event (such as stopPropagation() and preventDefault()) except they work exactly the same across all browsers.
If you find that you need the underlying browser event for some reason, simply use the nativeEvent attribute to get it.
-
ReactComponent
+
ReactComponent
Component classses created by createClass() return instances of ReactComponent when called. Most of the time when you're using React you're either creating or consuming ReactComponents.
@@ -149,7 +254,11 @@
When you're integrating with an external JavaScript application you may want to signal a change to a React component rendered with renderComponent(). Simply call setProps() to change its properties and trigger a re-render.
-
Note: This method can only be called on a root-level component. That is, it's only available on the component passed directly to renderComponent() and none of its children. If you're inclined to use setProps() on a child component, instead take advantage of reactive updates and pass the new prop to the child component when it's created in render().
+
+
Note:
+
+
This method can only be called on a root-level component. That is, it's only available on the component passed directly to renderComponent() and none of its children. If you're inclined to use setProps() on a child component, instead take advantage of reactive updates and pass the new prop to the child component when it's created in render().
Transfer properties from this component to a target component that have not already been set on the target component. This is usually used to pass down properties to the returned root component. targetComponent, now updated with some new props is returned as a convenience.
setState
-
setState(objectnextState)
+
setState(objectnextState[,functioncallback])
-
Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks.
+
Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. In addition, you can supply an optional callback function that is executed once setState is completed.
-
Note:NEVER mutate this.state directly. As calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
+
+
Note:
-
Note:setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
+
NEVER mutate this.state directly. As calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
+
+
Note:
+
+
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
+
+
Note:
+
+
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
+
replaceState
-
replaceState(objectnextState)
+
replaceState(objectnextState[,functioncallback])
Like setState() but deletes any pre-existing state keys that are not in nextState.
forceUpdate()
-
forceUpdate()
+
forceUpdate([functioncallback])
If your render() method reads from something other than this.props or this.state you'll need to tell React when it needs to re-run render(). Use forceUpdate() to cause React to automatically re-render. This will cause render() to be called on the component and all of its children but React will only update the DOM if the markup changes.
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your application much simpler and more efficient.
+
+
+
Note:
+
+
There is no guarantee of synchronous operation of calls to forceUpdate and calls may be batched for performance gains.
React has implemented a browser-independent events and DOM system for performance and cross-browser compatibility reasons. We took the opportunity to clean up a few rough edges in browser DOM implementations.
+
+
+
All events (including submit) bubble correctly per the W3C spec
+
All event objects conform to the W3C spec
+
All DOM properties and attributes (including event handlers) should be camelCased to be consistent with standard JavaScript style. We intentionally break with the spec here, since the spec is inconsistent.
+
The style attribute accepts a JavaScript object with camelCased properties rather than a CSS string. This is consistent with the DOM style JavaScript property, is more efficient, and prevents XSS security holes.
+
onChange behaves as you would expect it to: whenever a form field is changed this event is fired rather than inconsistently on blur. We intentionally break from existing browser behavior because onChange is a misnomer for its behavior and React relies on this event to react to user input in real time.
When designing interfaces, break down the common design elements (buttons, form fields, layout components, etc) into reusable components with well-defined interfaces. That way, the next time you need to build some UI you can write much less code, which means faster development time, less bugs, and less bytes down the wire.
+
+
Prop Validation
+
+
As your app grows it's helpful to ensure that your components are used correctly. We do this by allowing you to specify propTypes. React.PropTypes exports a range of validators that can be used to make sure the data you receive is valid. When an invalid value is provided for a prop, an error will be thrown. Here is an example documenting the different validators provided:
+
React.createClass({
+ propTypes:{
+ // You can declare that a prop is a specific JS primitive. By default, these
+ // are all optional.
+ optionalArray:React.PropTypes.array,
+ optionalBool:React.PropTypes.bool,
+ optionalFunc:React.PropTypes.func,
+ optionalNumber:React.PropTypes.number,
+ optionalObject:React.PropTypes.object,
+ optionalString:React.PropTypes.string,
+
+ // You can ensure that your prop is limited to specific values by treating
+ // it as an enum.
+ optionalEnum:React.PropTypes.oneOf(['News','Photos']),
+
+ // You can also declare that a prop is an instance of a class. This uses
+ // JS's instanceof operator.
+ someClass:React.PropTypes.instanceOf(SomeClass),
+
+ // You can chain any of the above with isRequired to make sure an error is
+ // thrown if the prop isn't provide.
+ requiredFunc:React.PropTypes.func.isRequired
+
+ // You can also specify a custom validator.
+ customProp:function(props,propName,componentName){
+ if(!/matchme/.test(props[propName])){
+ thrownewError('Validation failed!')
+ }
+ }
+ },
+ /* ... */
+});
+
+
Default Prop Values
+
+
React lets you define default values for your props in a very declarative way:
The result of getDefaultProps() will be cached and used to ensure that this.props.value will have a value if it was not specified by the parent component. This allows you to safely just use your props without having to write repetitive and fragile code to handle that yourself.
+
+
Transferring Props: A Shortcut
+
+
A common type of React component is one that extends a basic HTML in a simple way. Often you'll want to copy any HTML attributes passed to your component to the underlying HTML element to save typing. React provides transferPropsTo() to do just this.
+
/** @jsx React.DOM */
+
+varCheckLink=React.createClass({
+ render:function(){
+ // transferPropsTo() will take any props pased to CheckLink
+ // and copy them to <a>
+ returnthis.transferPropsTo(<a>{'√ '}{this.props.children}</a>);
+ }
+});
+
+React.renderComponent(
+ <CheckLinkhref="javascript:alert('Hello, world!');">
+ Clickhere!
+ </CheckLink>,
+ document.getElementById('example')
+);
+
+
Mixins
+
+
Components are the best way to reuse code in React, but sometimes very different components may share some common functionality. These are sometimes called cross-cutting concerns. React provides mixins to solve this problem.
+
+
One common use case is a component wanting to update itself on a time interval. It's easy to use setInterval(), but it's important to cancel your interval when you don't need it anymore to save memory. React provides lifecycle methods that let you know when a component is about to be created or destroyed. Let's create a simple mixin that uses these methods to provide an easy setInterval() function that will automatically get cleaned up when your component is destroyed.
A nice feature of mixins is that if a component is using multiple mixins and several mixins define the same lifecycle method (i.e. several mixins want to do some cleanup when the component is destroyed), all of the lifecycle methods are guaranteed to be called.
Every project uses a different system for building and deploying JavaScript. We've tried to make React as environment-agnostic as possible.
+
+
CDN-hosted React
+
+
We provide CDN-hosted versions of React on our download page. These prebuilt files use the UMD module format. Dropping them in with a simple <script> tag will inject a React global into your environment. It should also work out-of-the-box in CommonJS and AMD environments.
+
+
Using master
+
+
We have instructions for building from masterin our GitHub repository. We build a tree of CommonJS modules under build/modules which you can drop into any environment or packaging tool that supports CommonJS.
+
+
In-browser JSX Transform
+
+
If you like using JSX, we provide an in-browser JSX transformer for development on our download page. Simply include a <script type="text/jsx"> tag to engage the JSX transformer. Be sure to include the /** @jsx React.DOM */ comment as well, otherwise the transformer will not run the transforms.
+
+
+
Note:
+
+
The in-browser JSX transformer is fairly large and results in extraneous computation client-side that can be avoided. Do not use it in production — see the next section.
+
+
+
Productionizing: Precompiled JSX
+
+
If you have npm, you can simply run npm install -g react-tools to install our command-line jsx tool. This tool will translate files that use JSX syntax to plain JavaScript files that can run directly in the browser. It will also watch directories for you and automatically transform files when they are changed; for example: jsx --watch src/ build/. Run jsx --help for more information on how to use this tool.
+
+
Helpful Open-Source Projects
+
+
The open-source community has built tools that integrate JSX with several build systems.
The first thing you'll notice is the XML-ish syntax in your JavaScript. We have a simple precompiler that translates the syntactic sugar to this plain JavaScript:
// tutorial1-raw.js
@@ -178,7 +267,7 @@
Its use is optional but we've found JSX syntax easier to use than plain JavaScript. Read more on the JSX Syntax article.
-
What's going on
+
What's going on
We pass some methods in a JavaScript object to React.createClass() to create a new React component. The most important of these methods is called render which returns a tree of React components that will eventually render to HTML.
@@ -188,7 +277,7 @@
React.renderComponent() instantiates the root component, starts the framework, and injects the markup into a raw DOM element, provided as the second argument.
-
Composing components
+
Composing components
Let's build skeletons for CommentList and CommentForm which will, again, be simple <div>s:
// tutorial2.js
@@ -228,7 +317,7 @@
Notice how we're mixing HTML tags and components we've built. HTML components are regular React components, just like the ones you define, with one difference. The JSX compiler will automatically rewrite HTML tags to "React.DOM.tagName" expressions and leave everything else alone. This is to prevent the pollution of the global namespace.
-
Component Properties
+
Component Properties
Let's create our third component, Comment. We will want to pass it the author name and comment text so we can reuse the same code for each unique comment. First let's add some comments to the CommentList:
// tutorial4.js
@@ -245,7 +334,7 @@
Note that we have passed some data from the parent CommentList component to the child Comment component as both XML-like children and attributes. Data passed from parent to child is called props, short for properties.
-
Using props
+
Using props
Let's create the Comment component. It will read the data passed to it from the CommentList and render some markup:
// tutorial5.js
@@ -264,7 +353,7 @@
By surrounding a JavaScript expression in braces inside JSX (as either an attribute or child), you can drop text or React components into the tree. We access named attributes passed to the component as keys on this.props and any nested elements as this.props.children.
-
Adding Markdown
+
Adding Markdown
Markdown is a simple way to format your text inline. For example, surrounding text with asterisks will make it emphasized.
@@ -311,7 +400,7 @@
Remember: by using this feature you're relying on Showdown to be secure.
-
Hook up the data model
+
Hook up the data model
So far we've been inserting the comments directly in the source code. Instead, let's render a blob of JSON data into the comment list. Eventually this will come from the server, but for now, write it in your source:
// tutorial8.js
@@ -356,7 +445,7 @@
That's it!
-
Fetching from the server
+
Fetching from the server
Let's replace the hard-coded data with some dynamic data from the server. We will remove the data prop and replace it with a URL to fetch:
// tutorial11.js
@@ -367,7 +456,7 @@
This component is different from the prior components because it will have to re-render itself. The component won't have any data until the request from the server comes back, at which point the component may need to render some new comments.
-
Reactive state
+
Reactive state
So far, each component has rendered itself once based on its props. props are immutable: they are passed from the parent and are "owned" by the parent. To implement interactions, we introduce mutable state to the component. this.state is private to the component and can be changed by calling this.setState(). When the state is updated, the component re-renders itself.
@@ -392,7 +481,7 @@
getInitialState() executes exactly once during the lifecycle of the component and sets up the initial state of the component.
-
Updating state
+
Updating state
When the component is first created, we want to GET some JSON from the server and update the state to reflect the latest data. In a real application this would be a dynamic endpoint, but for this example, we will use a static JSON file to keep things simple:
// tutorial13.json
@@ -469,7 +558,7 @@
All we have done here is move the AJAX call to a separate method and call it when the component is first loaded and every 5 seconds after that. Try running this in your browser and changing the comments.json file; within 5 seconds, the changes will show!
-
Adding new comments
+
Adding new comments
Now it's time to build the form. Our CommentForm component should ask the user for their name and comment text and send a request to the server to save the comment.
// tutorial15.js
@@ -488,7 +577,7 @@
Let's make the form interactive. When the user submits the form, we should clear it, submit a request to the server, and refresh the list of comments. To start, let's listen for the form's submit event and clear it.
React attaches event handlers to components using a camelCase naming convention. We attach an onSubmit handler to the form that clears the form fields when the form is submitted with valid input.
We always return false from the event handler to prevent the browser's default action of submitting the form. (If you prefer, you can instead take the event as an argument and call preventDefault() on it – read more about event handling.)
-
React.autoBind() is a simple way to ensure that a method is always bound to its component. Inside the method, this will be bound to the component instance.
-
-
Refs
+
Refs
We use the ref attribute to assign a name to a child component and this.refs to reference the component. We can call getDOMNode() on a component to get the native browser DOM element.
-
Callbacks as props
+
Callbacks as props
When a user submits a comment, we will need to refresh the list of comments to include the new one. It makes sense to do all of this logic in CommentBox since CommentBox owns the state that represents the list of comments.
@@ -543,9 +630,9 @@
}.bind(this)});},
-handleCommentSubmit:React.autoBind(function(comment){
+handleCommentSubmit:function(comment){// TODO: submit to the server and refresh the list
-}),
+},getInitialState:function(){return{data:[]};},
@@ -572,14 +659,14 @@
Let's call the callback from the CommentForm when the user submits the form:
Our application is now feature complete but it feels slow to have to wait for the request to complete before your comment appears in the list. We can optimistically add this comment to the list to make the app feel faster.
React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the V in MVC.
+
+
We built React to solve one problem: building large applications with data that changes over time. To do this, React uses two main ideas.
+
+
Simple
+
+
Simply express how your app should look at any given point in time, and React will automatically manage all UI updates when your underlying data changes.
+
+
Declarative
+
+
When the data changes, React conceptually hits the "refresh" button, and knows to only update the changed parts.
+
+
Build Composable Components
+
+
React is all about building reusable components. In fact, with React the only thing you do is build components. Since they're so encapsulated, components make code reuse, testing, and separation of concerns easy.
+
+
Give It Five Minutes
+
+
React challenges a lot of conventional wisdom, and at first glance some of the ideas may seem crazy. Give it five minutes while reading this guide; those crazy ideas have worked for building thousands of components both inside and outside of Facebook and Instagram.
+
+
Learn More
+
+
You can learn more about our motivations behind building React in this blog post.
React provides powerful abstractions that free you from touching the DOM directly in most cases, but sometimes you simply need to access the underlying API, perhaps to work with a third-party library or existing code.
+
+
The Mock DOM
+
+
React is so fast because it never talks to the DOM directly. React maintains a fast in-memory representation of the DOM. render() methods return a description of the DOM, and React can diff this description with the in-memory representation to compute the fastest way to update the browser.
+
+
Additionally, React implements a full synthetic event system such that all event objects are guaranteed to conform to the W3C spec despite browser quirks, and everything bubbles consistently and in a performant way cross-browser. You can even use some HTML5 events in IE8!
+
+
Most of the time you should stay within React's "faked browser" world since it's more performant and easier to reason about. However, sometimes you simply need to access the underlying API, perhaps to work with a third-party library like a jQuery plugin. React provides escape hatches for you to use the underlying DOM API directly.
+
+
Refs and getDOMNode()
+
+
To interact with the browser, you'll need a reference to a DOM node. Every mounted React component has a getDOMNode() function which you can call to get a reference to it.
+
+
+
Note:
+
+
getDOMNode() only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling getDOMNode() in render() on a component that has yet to be created) an exception will be thrown.
+
+
+
In order to get a reference to a React component, you can either use this to get the current React component, or you can use refs to refer to a component you own. They work like this:
+
/** @jsx React.DOM */
+
+varMyComponent=React.createClass({
+ handleClick:function(){
+ // Explicitly focus the text input using the raw DOM API.
+ this.refs.myTextInput.getDOMNode().focus();
+ },
+ render:function(){
+ // The ref attribute adds a reference to the component to
+ // this.refs when the component is mounted.
+ return(
+ <div>
+ <inputtype="text"ref="myTextInput"/>
+ <input
+ type="button"
+ value="Focus the text input"
+ onClick={this.handleClick}
+ />
+ </div>
+ );
+ }
+});
+
+React.renderComponent(
+ <MyComponent/>,
+ document.getElementById('example')
+);
+
+
More About Refs
+
+
To learn more about refs, including ways to use them effectively, see our more about refs documentation.
+
+
Component Lifecycle
+
+
Components have three main parts of their lifecycle:
+
+
+
Mounting: A component is being inserted into the DOM.
+
Updating: A component is being re-rendered to determine if the DOM should be updated.
+
Unmounting: A component is being removed from the DOM.
+
+
+
React provides lifecycle methods that you can specify to hook into this process. We provide will methods, which are called right before something happens, and did methods which are called right after something happens.
+
+
Mounting
+
+
+
getInitialState(): object is invoked before a component is mounted. Stateful components should implement this and return the initial state data.
+
componentWillMount() is invoked immediately before mounting occurs.
+
componentDidMount(DOMElement rootNode) is invoked immediately after mounting occurs. Initialization that requires DOM nodes should go here.
+
+
+
Updating
+
+
+
componentWillReceiveProps(object nextProps) is invoked when a mounted component receives new props. This method should be used to compare this.props and nextProps to perform state transitions using this.setState().
+
shouldComponentUpdate(object nextProps, object nextState): boolean is invoked when a component decides whether any changes warrant an update to the DOM. Implement this as an optimization to compare this.props with nextProps and this.state with nextState and return false if React should skip updating.
+
componentWillUpdate(object nextProps, object nextState) is invoked immediately before updating occurs. You cannot call this.setState() here.
+
componentDidUpdate(object prevProps, object prevState, DOMElement rootNode) is invoked immediately after updating occurs.
+
+
+
Unmounting
+
+
+
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Cleanup should go here.
+
+
+
Mounted Methods
+
+
Mounted composite components also support the following methods:
+
+
+
getDOMNode(): DOMElement can be invoked on any mounted component in order to obtain a reference to its rendered DOM node.
+
forceUpdate() can be invoked on any mounted component when you know that some deeper aspect of the component's state has changed without using this.setState().
+
+
+
+
Note:
+
+
The DOMElement rootNode argument of componentDidMount() and
+componentDidUpdate() is a convenience. The same node can be obtained by
+calling this.getDOMNode().
+
+
+
Browser Suppport and Polyfills
+
+
At Facebook, we support older browsers, including IE8. We've had polyfills in place for a long time to allow us to write forward-thinking JS. This means we don't have a bunch of hacks scattered throughout our codebase and we can still expect our code to "just work". For example, instead of seeing +new Date(), we can just write Date.now(). Since the open source React is the same as what we use internally, we've carried over this philosophy of using forward thinking JS.
+
+
In addition to that philosphy, we've also taken the stance that we, as authors of a JS library, should not be shipping polyfills as a part of our library. If every library did this, there's a good chance you'd be sending down the same polyfill multiple times, which could be a sizable chunk of dead code. If your product needs to support older browsers, chances are you're already using something like es5-shim.
0.3.3 Upgrade react-tools dependencies, improve in-browser JSX transformer, make React.renderComponent more versatile. Details...
-
-
0.3.2 Improve compatibility of JSX Transformer; make react-tools compatible with browserify
-
-
0.3.1 Fix react-tools module
-
-
0.3 Initial public release.
-
-
0.2 Standardize API & refactor component lifecycle. Normalize DOM interactions.
-
-
0.1 Initial release.
-
diff --git a/downloads/react-0.4.0.zip b/downloads/react-0.4.0.zip
new file mode 100644
index 0000000000..ebbcf6dfb7
Binary files /dev/null and b/downloads/react-0.4.0.zip differ
diff --git a/feed.xml b/feed.xml
index 87c35fc8d9..cd26096952 100644
--- a/feed.xml
+++ b/feed.xml
@@ -6,6 +6,52 @@
http://facebook.github.io/react
+
+ React v0.4.0
+ <p>Over the past 2 months we've been taking feedback and working hard to make React even better. We've also added several features that awe didn't finish in time for the v0.3 launch, and are proud to announce the availability of React v0.4 today!</p>
+
+<p>This release could not have happened without the support of our growing community. Since launch day, the community has contributed blog posts, questions to the <a href="http://groups.google.com/group/reactjs">Google Group</a>, and issues and pull requests on GitHub. We've had contributions ranging from documentation improvements to major changes to React's rendering. We've seen people integrate React into the tools they're using and the products they're building, and we're all very excited to see what our budding community builds next!</p>
+
+<p>React v0.4 has some big changes. We've also restructured the documentation to better communicate how to use React. We've summarized the changes below and linked to documentation where we think it will be especially useful.</p>
+
+<p>When you're ready, <a href="/react/downloads.html">go download it</a>!</p>
+
+<h3>React</h3>
+
+<ul>
+<li>Switch from using <code>id</code> attribute to <code>data-reactid</code> to track DOM nodes. This allows you to integrate with other JS and CSS libraries more easily.</li>
+<li>Support for more DOM elements and attributes (e.g., <code><canvas></code>)</li>
+<li>Improved server-side rendering APIs. <code>React.renderComponentToString(<component>, callback)</code> allows you to use React on the server and generate markup which can be sent down to the browser.</li>
+<li><code>prop</code> improvements: validation and default values. <a href="http://facebook.github.io/react/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.html">Read our blog post for details...</a></li>
+<li>Support for the <code>key</code> prop, which allows for finer control over reconciliation. <a href="http://facebook.github.io/react/docs/multiple-components.html">Read the docs for details...</a></li>
+<li>Removed <code>React.autoBind</code>. <a href="http://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html">Read our blog post for details...</a></li>
+<li>Improvements to forms. We've written wrappers around <code><input></code>, <code><textarea></code>, <code><option></code>, and <code><select></code> in order to standardize many inconsistencies in browser implementations. This includes support for <code>defaultValue</code>, and improved implementation of the <code>onChange</code> event, and circuit completion. <a href="http://facebook.github.io/react/docs/forms.html">Read the docs for details...</a></li>
+<li>We've implemented an improved synthetic event system that conforms to the W3C spec.</li>
+<li>Updates to your component are batched now, which may result in a significantly faster re-render of components. <code>this.setState</code> now takes an optional callback as it's second parameter. If you were using <code>onClick={this.setState.bind(this, state)}</code> previously, you'll want to make sure you add a third parameter so that the event is not treated as the callback.</li>
+</ul>
+
+<h3>JSX</h3>
+
+<ul>
+<li>Support for comment nodes <code><div>{/* this is a comment and won't be rendered */}</div></code></li>
+<li>Children are now transformed directly into arguments instead of being wrapped in an array
+E.g. <code><div><Component1/><Component2></div></code> is transformed into <code>React.DOM.div(null, Component1(null), Component2(null))</code>.
+Previously this would be transformed into <code>React.DOM.div(null, [Component1(null), Component2(null)])</code>.
+If you were using React without JSX previously, your code should still work.</li>
+</ul>
+
+<h3>react-tools</h3>
+
+<ul>
+<li>Fixed a number of bugs when transforming directories</li>
+<li>No longer re-write <code>require()</code>s to be relative unless specified</li>
+</ul>
+
+ 2013-07-17T00:00:00-07:00
+ http://facebook.github.io/react/blog/2013/07/17/react-v0-4-0.html
+ http://facebook.github.io/react/blog/2013/07/17/react-v0-4-0.html
+
+
New in React v0.4: Prop Validation and Default Values<p>Many of the questions we got following the public launch of React revolved around <code>props</code>, specifically that people wanted to do validation and to make sure their components had sensible defaults.</p>
diff --git a/index.html b/index.html
index f8fe51b283..41df54b78d 100644
--- a/index.html
+++ b/index.html
@@ -59,7 +59,7 @@
diff --git a/js/JSXTransformer.js b/js/JSXTransformer.js
index 9858ee430d..2e4d7b5265 100644
--- a/js/JSXTransformer.js
+++ b/js/JSXTransformer.js
@@ -1,719 +1,268 @@
/**
- * JSXTransformer v0.3.3
+ * JSXTransformer v0.4.0
*/
(function(e){if("function"==typeof bootstrap)bootstrap("jsxtransformer",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeJSXTransformer=e}else"undefined"!=typeof window?window.JSXTransformer=e():global.JSXTransformer=e()})(function(){var define,ses,bootstrap,module,exports;
return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 0) {
+ s = this.chars[i % 62] + s
+ i = Math.floor(i/62)
}
+ return s
};
- return xhr.send(null);
-};
-
-runScripts = function() {
- var scripts = document.getElementsByTagName('script');
- scripts = Array.prototype.slice.call(scripts);
- var jsxScripts = scripts.filter(function(script) {
- return script.type === 'text/jsx';
- });
-
- jsxScripts.forEach(function(script) {
- if (script.src) {
- load(script.src);
- } else {
- run(script.innerHTML);
- }
- });
-};
-
-if (window.addEventListener) {
- window.addEventListener('DOMContentLoaded', runScripts, false);
-} else {
- window.attachEvent('onload', runScripts);
-}
-
-},{"./fbtransform/lib/transform":2,"./fbtransform/visitors":3,"./fbtransform/lib/docblock":4}],4:[function(require,module,exports){
-/**
- * Copyright 2013 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var docblockRe = /^\s*(\/\*\*(.|\n)*?\*\/)/;
-var ltrimRe = /^\s*/;
-/**
- * @param {String} contents
- * @return {String}
- */
-function extract(contents) {
- var match = contents.match(docblockRe);
- if (match) {
- return match[0].replace(ltrimRe, '') || '';
- }
- return '';
-}
-
-
-var commentStartRe = /^\/\*\*?/;
-var commentEndRe = /\*\/$/;
-var wsRe = /[\t ]+/g;
-var stringStartRe = /(\n|^) *\*/g;
-var multilineRe = /(?:^|\n) *(@[^\n]*?) *\n *([^@\n\s][^@\n]+?) *\n/g;
-var propertyRe = /(?:^|\n) *@(\S+) *([^\n]*)/g;
-
-/**
- * @param {String} contents
- * @return {Array}
- */
-function parse(docblock) {
- docblock = docblock
- .replace(commentStartRe, '')
- .replace(commentEndRe, '')
- .replace(wsRe, ' ')
- .replace(stringStartRe, '$1');
-
- // Normalize multi-line directives
- var prev = '';
- while (prev != docblock) {
- prev = docblock;
- docblock = docblock.replace(multilineRe, "\n$1 $2\n");
- }
- docblock = docblock.trim();
-
- var result = [];
- var match;
- while (match = propertyRe.exec(docblock)) {
- result.push([match[1], match[2]]);
- }
-
- return result;
-}
-
-/**
- * Same as parse but returns an object of prop: value instead of array of paris
- * If a property appers more than once the last one will be returned
- *
- * @param {String} contents
- * @return {Object}
- */
-function parseAsObject(docblock) {
- var pairs = parse(docblock);
- var result = {};
- for (var i = 0; i < pairs.length; i++) {
- result[pairs[i][0]] = pairs[i][1];
- }
- return result;
-}
-
-
-exports.extract = extract;
-exports.parse = parse;
-exports.parseAsObject = parseAsObject;
-
-},{}],3:[function(require,module,exports){
-(function(){/*global exports:true*/
-var classes = require('./transforms/classes');
-var react = require('./transforms/react');
-var reactDisplayName = require('./transforms/reactDisplayName');
-
-/**
- * Map from transformName => orderedListOfVisitors.
- */
-var transformVisitors = {
- 'es6-classes': [
- classes.visitClassExpression,
- classes.visitClassDeclaration,
- classes.visitSuperCall,
- classes.visitPrivateProperty
- ]
-};
-
-transformVisitors.react = transformVisitors[
- "es6-classes"
-].concat([
- react.visitReactTag,
- reactDisplayName.visitReactDisplayName
-]);
-
-/**
- * Specifies the order in which each transform should run.
- */
-var transformRunOrder = [
- 'es6-classes',
- 'react'
-];
-
-/**
- * Given a list of transform names, return the ordered list of visitors to be
- * passed to the transform() function.
- *
- * @param {array?} excludes
- * @return {array}
- */
-function getVisitorsList(excludes) {
- var ret = [];
- for (var i = 0, il = transformRunOrder.length; i < il; i++) {
- if (!excludes || excludes.indexOf(transformRunOrder[i]) === -1) {
- ret = ret.concat(transformVisitors[transformRunOrder[i]]);
- }
- }
- return ret;
-}
-
-exports.getVisitorsList = getVisitorsList;
-exports.transformVisitors = transformVisitors;
-
-})()
-},{"./transforms/classes":5,"./transforms/react":6,"./transforms/reactDisplayName":7}],8:[function(require,module,exports){
-(function(){/**
- * Copyright 2013 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*global exports:true*/
-
-/**
- * State represents the given parser state. It has a local and global parts.
- * Global contains parser position, source, etc. Local contains scope based
- * properties, like current class name. State should contain all the info
- * required for transformation. It's the only mandatory object that is being
- * passed to every function in transform chain.
- *
- * @param {String} source
- * @param {Object} transformOptions
- * @return {Object}
- */
-function createState(source, transformOptions) {
- return {
- /**
- * Name of the super class variable
- * @type {String}
- */
- superVar: '',
- /**
- * Name of the enclosing class scope
- * @type {String}
- */
- scopeName: '',
- /**
- * Global state (not affected by updateState)
- * @type {Object}
- */
- g: {
- /**
- * A set of general options that transformations can consider while doing
- * a transformation:
- *
- * - minify
- * Specifies that transformation steps should do their best to minify
- * the output source when possible. This is useful for places where
- * minification optimizations are possible with higher-level context
- * info than what jsxmin can provide.
- *
- * For example, the ES6 class transform will minify munged private
- * variables if this flag is set.
- */
- opts: transformOptions,
- /**
- * Current position in the source code
- * @type {Number}
- */
- position: 0,
- /**
- * Buffer containing the result
- * @type {String}
- */
- buffer: '',
- /**
- * Indentation offset (only negative offset is supported now)
- * @type {Number}
- */
- indentBy: 0,
- /**
- * Source that is being transformed
- * @type {String}
- */
- source: source,
-
- /**
- * Cached parsed docblock (see getDocblock)
- * @type {object}
- */
- docblock: null,
-
- /**
- * Whether the thing was used
- * @type {Boolean}
- */
- tagNamespaceUsed: false,
-
- /**
- * If using bolt xjs transformation
- * @type {Boolean}
- */
- isBolt: undefined,
-
- /**
- * Whether to record source map (expensive) or not
- * @type {SourceMapGenerator|null}
- */
- sourceMap: null,
-
- /**
- * Filename of the file being processed. Will be returned as a source
- * attribute in the source map
- */
- sourceMapFilename: 'source.js',
-
- /**
- * Only when source map is used: last line in the source for which
- * source map was generated
- * @type {Number}
- */
- sourceLine: 1,
-
- /**
- * Only when source map is used: last line in the buffer for which
- * source map was generated
- * @type {Number}
- */
- bufferLine: 1,
-
- /**
- * The top-level Program AST for the original file.
- */
- originalProgramAST: null,
-
- sourceColumn: 0,
- bufferColumn: 0
- }
+ 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
};
-}
-/**
- * Updates a copy of a given state with "update" and returns an updated state.
- *
- * @param {Object} state
- * @param {Object} update
- * @return {Object}
- */
-function updateState(state, update) {
- return {
- g: state.g,
- superVar: update.superVar || state.superVar,
- scopeName: update.scopeName || state.scopeName
- };
-}
+ return my;
+}({}));
-/**
- * Given a state fill the resulting buffer from the original source up to
- * the end
- * @param {Number} end
- * @param {Object} state
- * @param {Function?} contentTransformer Optional callback to transform newly
- * added content.
- */
-function catchup(end, state, contentTransformer) {
- if (end < state.g.position) {
- // cannot move backwards
- return;
- }
- var source = state.g.source.substring(state.g.position, end);
- var transformed = updateIndent(source, state);
- if (state.g.sourceMap && transformed) {
- // record where we are
- state.g.sourceMap.addMapping({
- generated: { line: state.g.bufferLine, column: state.g.bufferColumn },
- original: { line: state.g.sourceLine, column: state.g.sourceColumn },
- source: state.g.sourceMapFilename
- });
-
- // record line breaks in transformed source
- var sourceLines = source.split('\n');
- var transformedLines = transformed.split('\n');
- // Add line break mappings between last known mapping and the end of the
- // added piece. So for the code piece
- // (foo, bar);
- // > var x = 2;
- // > var b = 3;
- // var c =
- // only add lines marked with ">": 2, 3.
- for (var i = 1; i < sourceLines.length - 1; i++) {
- state.g.sourceMap.addMapping({
- generated: { line: state.g.bufferLine, column: 0 },
- original: { line: state.g.sourceLine, column: 0 },
- source: state.g.sourceMapFilename
- });
- state.g.sourceLine++;
- state.g.bufferLine++;
+module.exports = Base62
+},{}],2:[function(require,module,exports){
+(function(process){function filter (xs, fn) {
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ if (fn(xs[i], i, xs)) res.push(xs[i]);
}
- // offset for the last piece
- if (sourceLines.length > 1) {
- state.g.sourceLine++;
- state.g.bufferLine++;
- state.g.sourceColumn = 0;
- state.g.bufferColumn = 0;
- }
- state.g.sourceColumn += sourceLines[sourceLines.length - 1].length;
- state.g.bufferColumn +=
- transformedLines[transformedLines.length - 1].length;
- }
- state.g.buffer +=
- contentTransformer ? contentTransformer(transformed) : transformed;
- state.g.position = end;
+ return res;
}
-/**
- * Applies `catchup` but passing in a function that removes any non-whitespace
- * characters.
- */
-var re = /(\S)/g;
-function stripNonWhite(value) {
- return value.replace(re, function() {
- return '';
- });
-}
-/**
- * Catches up as `catchup` but turns each non-white character into a space.
- */
-function catchupWhiteSpace(end, state) {
- catchup(end, state, stripNonWhite);
-}
-
-/**
- * Same as catchup but does not touch the buffer
- * @param {Number} end
- * @param {Object} state
- */
-function move(end, state) {
- // move the internal cursors
- if (state.g.sourceMap) {
- if (end < state.g.position) {
- state.g.position = 0;
- state.g.sourceLine = 1;
- state.g.sourceColumn = 0;
- }
-
- var source = state.g.source.substring(state.g.position, end);
- var sourceLines = source.split('\n');
- if (sourceLines.length > 1) {
- state.g.sourceLine += sourceLines.length - 1;
- state.g.sourceColumn = 0;
- }
- state.g.sourceColumn += sourceLines[sourceLines.length - 1].length;
- }
- state.g.position = end;
-}
-
-/**
- * Appends a string of text to the buffer
- * @param {String} string
- * @param {Object} state
- */
-function append(string, state) {
- if (state.g.sourceMap && string) {
- state.g.sourceMap.addMapping({
- generated: { line: state.g.bufferLine, column: state.g.bufferColumn },
- original: { line: state.g.sourceLine, column: state.g.sourceColumn },
- source: state.g.sourceMapFilename
- });
- var transformedLines = string.split('\n');
- if (transformedLines.length > 1) {
- state.g.bufferLine += transformedLines.length - 1;
- state.g.bufferColumn = 0;
- }
- state.g.bufferColumn +=
- transformedLines[transformedLines.length - 1].length;
- }
- state.g.buffer += string;
-}
-
-/**
- * Update indent using state.indentBy property. Indent is measured in
- * double spaces. Updates a single line only.
- *
- * @param {String} str
- * @param {Object} state
- * @return {String}
- */
-function updateIndent(str, state) {
- for (var i = 0; i < -state.g.indentBy; i++) {
- str = str.replace(/(^|\n)( {2}|\t)/g, '$1');
- }
- return str;
-}
-
-/**
- * Calculates indent from the beginning of the line until "start" or the first
- * character before start.
- * @example
- * " foo.bar()"
- * ^
- * start
- * indent will be 2
- *
- * @param {Number} start
- * @param {Object} state
- * @return {Number}
- */
-function indentBefore(start, state) {
- var end = start;
- start = start - 1;
-
- while (start > 0 && state.g.source[start] != '\n') {
- if (!state.g.source[start].match(/[ \t]/)) {
- end = start;
- }
- start--;
- }
- return state.g.source.substring(start + 1, end);
-}
-
-function getDocblock(state) {
- if (!state.g.docblock) {
- var docblock = require('./docblock');
- state.g.docblock =
- docblock.parseAsObject(docblock.extract(state.g.source));
- }
- return state.g.docblock;
-}
-
-exports.catchup = catchup;
-exports.catchupWhiteSpace = catchupWhiteSpace;
-exports.append = append;
-exports.move = move;
-exports.updateIndent = updateIndent;
-exports.indentBefore = indentBefore;
-exports.updateState = updateState;
-exports.createState = createState;
-exports.getDocblock = getDocblock;
-
-})()
-},{"./docblock":4}],2:[function(require,module,exports){
-(function(){/**
- * Copyright 2013 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*global exports:true*/
-/*jslint node: true*/
-"use strict";
-
-/**
- * Syntax transfomer for javascript. Takes the source in, spits the source
- * out. Tries to maintain readability and preserve whitespace and line numbers
- * where posssible.
- *
- * Support
- * - ES6 class transformation + private property munging, see ./classes.js
- * - React XHP style syntax transformations, see ./react.js
- * - Bolt XHP style syntax transformations, see ./bolt.js
- *
- * The general flow is the following:
- * - Parse the source with our customized esprima-parser
- * https://github.com/voloko/esprima. We have to customize the parser to
- * support non-standard XHP-style syntax. We parse the source range: true
- * option that forces esprima to return positions in the source within
- * resulting parse tree.
- *
- * - Traverse resulting syntax tree, trying to apply a set of visitors to each
- * node. Each visitor should provide a .test() function that tests if the
- * visitor can process a given node.
- *
- * - Visitor is responsible for code generation for a given syntax node.
- * Generated code is stored in state.g.buffer that is passed to every
- * visitor. It's up to the visitor to process the code the way it sees fit.
- * All of the current visitors however use both the node and the original
- * source to generate transformed code. They use nodes to generate new
- * code and they copy the original source, preserving whitespace and comments,
- * for the parts they don't care about.
- */
-var esprima = require('esprima');
-
-var createState = require('./utils').createState;
-var catchup = require('./utils').catchup;
-
-/**
- * @param {object} object
- * @param {function} visitor
- * @param {array} path
- * @param {object} state
- */
-function traverse(object, path, state) {
- var key, child;
-
- if (walker(traverse, object, path, state) === false) {
- return;
- }
- path.unshift(object);
- for (key in object) {
- // skip obviously wrong attributes
- if (key === 'range' || key === 'loc') {
- continue;
- }
- if (object.hasOwnProperty(key)) {
- child = object[key];
- if (typeof child === 'object' && child !== null) {
- child.range && catchup(child.range[0], state);
- traverse(child, path, state);
- child.range && catchup(child.range[1], state);
- }
+// resolves . and .. elements in a path array with directory names there
+// must be no slashes, empty elements, or device names (c:\) in the array
+// (so also no leading and trailing slashes - it does not distinguish
+// relative and absolute paths)
+function normalizeArray(parts, allowAboveRoot) {
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = parts.length; i >= 0; i--) {
+ var last = parts[i];
+ if (last == '.') {
+ parts.splice(i, 1);
+ } else if (last === '..') {
+ parts.splice(i, 1);
+ up++;
+ } else if (up) {
+ parts.splice(i, 1);
+ up--;
}
}
- path.shift();
-}
-function walker(traverse, object, path, state) {
- var visitors = state.g.visitors;
- for (var i = 0; i < visitors.length; i++) {
- if (visitors[i].test(object, path, state)) {
- return visitors[i](traverse, object, path, state);
+ // if the path is allowed to go above the root, restore leading ..s
+ if (allowAboveRoot) {
+ for (; up--; up) {
+ parts.unshift('..');
}
}
+
+ return parts;
}
-function runPass(source, visitors, options) {
- var ast = esprima.parse(source, { comment: true, loc: true, range: true });
- var state = createState(source, options);
- state.g.originalProgramAST = ast;
- state.g.visitors = visitors;
+// Regex to split a filename into [*, dir, basename, ext]
+// posix version
+var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
- if (options.sourceMap) {
- var SourceMapGenerator = require('source-map').SourceMapGenerator;
- state.g.sourceMap = new SourceMapGenerator({ file: 'transformed.js' });
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+var resolvedPath = '',
+ resolvedAbsolute = false;
+
+for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
+ var path = (i >= 0)
+ ? arguments[i]
+ : process.cwd();
+
+ // Skip empty and invalid entries
+ if (typeof path !== 'string' || !path) {
+ continue;
}
- traverse(ast, [], state);
- catchup(source.length, state);
- return state;
+
+ resolvedPath = path + '/' + resolvedPath;
+ resolvedAbsolute = path.charAt(0) === '/';
}
-/**
- * Applies all available transformations to the source
- * @param {array} visitors
- * @param {string} source
- * @param {?object} options
- * @return {object}
- */
-function transform(visitors, source, options) {
- options = options || {};
+// At this point the path should be resolved to a full absolute path, but
+// handle relative paths to be safe (might happen when process.cwd() fails)
- var state = runPass(source, visitors, options);
- var sourceMap = state.g.sourceMap;
+// Normalize the path
+resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+ return !!p;
+ }), !resolvedAbsolute).join('/');
- if (sourceMap) {
- return {
- sourceMap: sourceMap,
- sourceMapFilename: options.filename || 'source.js',
- code: state.g.buffer
- };
+ return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+var isAbsolute = path.charAt(0) === '/',
+ trailingSlash = path.slice(-1) === '/';
+
+// Normalize the path
+path = normalizeArray(filter(path.split('/'), function(p) {
+ return !!p;
+ }), !isAbsolute).join('/');
+
+ if (!path && !isAbsolute) {
+ path = '.';
+ }
+ if (path && trailingSlash) {
+ path += '/';
+ }
+
+ return (isAbsolute ? '/' : '') + path;
+};
+
+
+// posix version
+exports.join = function() {
+ var paths = Array.prototype.slice.call(arguments, 0);
+ return exports.normalize(filter(paths, function(p, index) {
+ return p && typeof p === 'string';
+ }).join('/'));
+};
+
+
+exports.dirname = function(path) {
+ var dir = splitPathRe.exec(path)[1] || '';
+ var isWindows = false;
+ if (!dir) {
+ // No dirname
+ return '.';
+ } else if (dir.length === 1 ||
+ (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
+ // It is just a slash or a drive letter with a slash
+ return dir;
} else {
- return {
- code: state.g.buffer
- };
+ // It is a full dirname, strip trailing slash
+ return dir.substring(0, dir.length - 1);
}
+};
+
+
+exports.basename = function(path, ext) {
+ var f = splitPathRe.exec(path)[2] || '';
+ // TODO: make this comparison case-insensitive on windows?
+ if (ext && f.substr(-1 * ext.length) === ext) {
+ f = f.substr(0, f.length - ext.length);
+ }
+ return f;
+};
+
+
+exports.extname = function(path) {
+ return splitPathRe.exec(path)[3] || '';
+};
+
+exports.relative = function(from, to) {
+ from = exports.resolve(from).substr(1);
+ to = exports.resolve(to).substr(1);
+
+ function trim(arr) {
+ var start = 0;
+ for (; start < arr.length; start++) {
+ if (arr[start] !== '') break;
+ }
+
+ var end = arr.length - 1;
+ for (; end >= 0; end--) {
+ if (arr[end] !== '') break;
+ }
+
+ if (start > end) return [];
+ return arr.slice(start, end - start + 1);
+ }
+
+ var fromParts = trim(from.split('/'));
+ var toParts = trim(to.split('/'));
+
+ var length = Math.min(fromParts.length, toParts.length);
+ var samePartsLength = length;
+ for (var i = 0; i < length; i++) {
+ if (fromParts[i] !== toParts[i]) {
+ samePartsLength = i;
+ break;
+ }
+ }
+
+ var outputParts = [];
+ for (var i = samePartsLength; i < fromParts.length; i++) {
+ outputParts.push('..');
+ }
+
+ outputParts = outputParts.concat(toParts.slice(samePartsLength));
+
+ return outputParts.join('/');
+};
+
+})(require("__browserify_process"))
+},{"__browserify_process":3}],3:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+
+process.nextTick = (function () {
+ var canSetImmediate = typeof window !== 'undefined'
+ && window.setImmediate;
+ var canPost = typeof window !== 'undefined'
+ && window.postMessage && window.addEventListener
+ ;
+
+ if (canSetImmediate) {
+ return function (f) { return window.setImmediate(f) };
+ }
+
+ if (canPost) {
+ var queue = [];
+ window.addEventListener('message', function (ev) {
+ if (ev.source === window && ev.data === 'process-tick') {
+ ev.stopPropagation();
+ if (queue.length > 0) {
+ var fn = queue.shift();
+ fn();
+ }
+ }
+ }, true);
+
+ return function nextTick(fn) {
+ queue.push(fn);
+ window.postMessage('process-tick', '*');
+ };
+ }
+
+ return function nextTick(fn) {
+ setTimeout(fn, 0);
+ };
+})();
+
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
}
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
-exports.transform = transform;
-
-})()
-},{"./utils":8,"esprima":9,"source-map":10}],9:[function(require,module,exports){
+},{}],4:[function(require,module,exports){
(function(){/*
Copyright (C) 2013 Thaddee Tyl
Copyright (C) 2012 Ariya Hidayat
@@ -759,7 +308,7 @@ parseSpreadOrAssignmentExpression: true,
parseStatement: true, parseSourceElement: true, parseModuleBlock: true, parseConciseBody: true,
advanceXJSChild: true, isXJSIdentifierStart: true, isXJSIdentifierPart: true,
scanXJSStringLiteral: true, scanXJSIdentifier: true,
-parseXJSAttributeValue: true, parseXJSChild: true, parseXJSElement: true, parseXJSExpression: true,
+parseXJSAttributeValue: true, parseXJSChild: true, parseXJSElement: true, parseXJSExpressionContainer: true, parseXJSEmptyExpression: true,
parseYieldExpression: true
*/
@@ -903,7 +452,8 @@ parseYieldExpression: true
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement',
XJSIdentifier: 'XJSIdentifier',
- XJSExpression: 'XJSExpression',
+ XJSEmptyExpression: 'XJSEmptyExpression',
+ XJSExpressionContainer: 'XJSExpressionContainer',
XJSElement: 'XJSElement',
XJSClosingElement: 'XJSClosingElement',
XJSOpeningElement: 'XJSOpeningElement',
@@ -2456,10 +2006,16 @@ parseYieldExpression: true
};
},
- createXJSExpression: function (expression) {
+ createXJSEmptyExpression: function () {
return {
- type: Syntax.XJSExpression,
- value: expression
+ type: Syntax.XJSEmptyExpression
+ };
+ },
+
+ createXJSExpressionContainer: function (expression) {
+ return {
+ type: Syntax.XJSExpressionContainer,
+ expression: expression
};
},
@@ -5960,8 +5516,15 @@ parseYieldExpression: true
function parseXJSAttributeValue() {
var value;
- if (lookahead.value === '{') {
- value = parseXJSExpression();
+ if (match('{')) {
+ value = parseXJSExpressionContainer();
+ if (value.expression.type === Syntax.XJSEmptyExpression) {
+ throwError(
+ value,
+ 'XJS attributes must only be assigned a non-empty ' +
+ 'expression'
+ );
+ }
} else if (lookahead.type === Token.XJSText) {
value = delegate.createLiteral(lex());
} else {
@@ -5970,8 +5533,15 @@ parseYieldExpression: true
return value;
}
- function parseXJSExpression() {
- var value, origInXJSChild, origInXJSTag;
+ function parseXJSEmptyExpression() {
+ while (source.charAt(index) !== '}') {
+ index++;
+ }
+ return delegate.createXJSEmptyExpression();
+ }
+
+ function parseXJSExpressionContainer() {
+ var expression, origInXJSChild, origInXJSTag;
origInXJSChild = state.inXJSChild;
origInXJSTag = state.inXJSTag;
@@ -5980,14 +5550,18 @@ parseYieldExpression: true
expect('{');
- value = parseExpression();
+ if (match('}')) {
+ expression = parseXJSEmptyExpression();
+ } else {
+ expression = parseExpression();
+ }
state.inXJSChild = origInXJSChild;
state.inXJSTag = origInXJSTag;
expect('}');
- return delegate.createXJSExpression(value);
+ return delegate.createXJSExpressionContainer(expression);
}
function parseXJSAttribute() {
@@ -6006,8 +5580,8 @@ parseYieldExpression: true
function parseXJSChild() {
var token;
- if (lookahead.value === '{') {
- token = parseXJSExpression();
+ if (match('{')) {
+ token = parseXJSExpressionContainer();
} else if (lookahead.type === Token.XJSText) {
token = delegate.createLiteral(lex());
} else {
@@ -6493,7 +6067,8 @@ parseYieldExpression: true
extra.parseXJSChild = parseXJSChild;
extra.parseXJSAttribute = parseXJSAttribute;
extra.parseXJSAttributeValue = parseXJSAttributeValue;
- extra.parseXJSExpression = parseXJSExpression;
+ extra.parseXJSExpressionContainer = parseXJSExpressionContainer;
+ extra.parseXJSEmptyExpression = parseXJSEmptyExpression;
extra.parseXJSElement = parseXJSElement;
extra.parseXJSClosingElement = parseXJSClosingElement;
extra.parseXJSOpeningElement = parseXJSOpeningElement;
@@ -6545,7 +6120,8 @@ parseYieldExpression: true
parseXJSChild = wrapTrackingPreserveWhitespace(extra.parseXJSChild);
parseXJSAttribute = wrapTracking(extra.parseXJSAttribute);
parseXJSAttributeValue = wrapTracking(extra.parseXJSAttributeValue);
- parseXJSExpression = wrapTracking(extra.parseXJSExpression);
+ parseXJSExpressionContainer = wrapTracking(extra.parseXJSExpressionContainer);
+ parseXJSEmptyExpression = wrapTrackingPreserveWhitespace(extra.parseXJSEmptyExpression);
parseXJSElement = wrapTracking(extra.parseXJSElement);
parseXJSClosingElement = wrapTracking(extra.parseXJSClosingElement);
parseXJSOpeningElement = wrapTracking(extra.parseXJSOpeningElement);
@@ -6614,7 +6190,8 @@ parseYieldExpression: true
parseXJSChild = extra.parseXJSChild;
parseXJSAttribute = extra.parseXJSAttribute;
parseXJSAttributeValue = extra.parseXJSAttributeValue;
- parseXJSExpression = extra.parseXJSExpression;
+ parseXJSExpressionContainer = extra.parseXJSExpressionContainer;
+ parseXJSEmptyExpression = extra.parseXJSEmptyExpression;
parseXJSElement = extra.parseXJSElement;
parseXJSClosingElement = extra.parseXJSClosingElement;
parseXJSOpeningElement = extra.parseXJSOpeningElement;
@@ -6869,7 +6446,2174 @@ parseYieldExpression: true
/* vim: set sw=4 ts=4 et tw=80 : */
})()
-},{}],11:[function(require,module,exports){
+},{}],5:[function(require,module,exports){
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
+exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
+exports.SourceNode = require('./source-map/source-node').SourceNode;
+
+},{"./source-map/source-map-consumer":10,"./source-map/source-map-generator":11,"./source-map/source-node":12}],6:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+
+ /**
+ * A data structure which is a combination of an array and a set. Adding a new
+ * member is O(1), testing for membership is O(1), and finding the index of an
+ * element is O(1). Removing elements from the set is not supported. Only
+ * strings are supported for membership.
+ */
+ function ArraySet() {
+ this._array = [];
+ this._set = {};
+ }
+
+ /**
+ * Static method for creating ArraySet instances from an existing array.
+ */
+ ArraySet.fromArray = function ArraySet_fromArray(aArray) {
+ var set = new ArraySet();
+ for (var i = 0, len = aArray.length; i < len; i++) {
+ set.add(aArray[i]);
+ }
+ return set;
+ };
+
+ /**
+ * Add the given string to this set.
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.add = function ArraySet_add(aStr) {
+ if (this.has(aStr)) {
+ // Already a member; nothing to do.
+ return;
+ }
+ var idx = this._array.length;
+ this._array.push(aStr);
+ this._set[util.toSetString(aStr)] = idx;
+ };
+
+ /**
+ * Is the given string a member of this set?
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.has = function ArraySet_has(aStr) {
+ return Object.prototype.hasOwnProperty.call(this._set,
+ util.toSetString(aStr));
+ };
+
+ /**
+ * What is the index of the given string in the array?
+ *
+ * @param String aStr
+ */
+ ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
+ if (this.has(aStr)) {
+ return this._set[util.toSetString(aStr)];
+ }
+ throw new Error('"' + aStr + '" is not in the set.');
+ };
+
+ /**
+ * What is the element at the given index?
+ *
+ * @param Number aIdx
+ */
+ ArraySet.prototype.at = function ArraySet_at(aIdx) {
+ if (aIdx >= 0 && aIdx < this._array.length) {
+ return this._array[aIdx];
+ }
+ throw new Error('No element indexed by ' + aIdx);
+ };
+
+ /**
+ * Returns the array representation of this set (which has the proper indices
+ * indicated by indexOf). Note that this is a copy of the internal array used
+ * for storing the members so that no one can mess with internal state.
+ */
+ ArraySet.prototype.toArray = function ArraySet_toArray() {
+ return this._array.slice();
+ };
+
+ exports.ArraySet = ArraySet;
+
+});
+
+},{"./util":13,"amdefine":14}],7:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var base64 = require('./base64');
+
+ // A single base 64 digit can contain 6 bits of data. For the base 64 variable
+ // length quantities we use in the source map spec, the first bit is the sign,
+ // the next four bits are the actual value, and the 6th bit is the
+ // continuation bit. The continuation bit tells us whether there are more
+ // digits in this value following this digit.
+ //
+ // Continuation
+ // | Sign
+ // | |
+ // V V
+ // 101011
+
+ var VLQ_BASE_SHIFT = 5;
+
+ // binary: 100000
+ var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+ // binary: 011111
+ var VLQ_BASE_MASK = VLQ_BASE - 1;
+
+ // binary: 100000
+ var VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+ /**
+ * Converts from a two-complement value to a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+ * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+ */
+ function toVLQSigned(aValue) {
+ return aValue < 0
+ ? ((-aValue) << 1) + 1
+ : (aValue << 1) + 0;
+ }
+
+ /**
+ * Converts to a two-complement value from a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+ * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+ */
+ function fromVLQSigned(aValue) {
+ var isNegative = (aValue & 1) === 1;
+ var shifted = aValue >> 1;
+ return isNegative
+ ? -shifted
+ : shifted;
+ }
+
+ /**
+ * Returns the base 64 VLQ encoded value.
+ */
+ exports.encode = function base64VLQ_encode(aValue) {
+ var encoded = "";
+ var digit;
+
+ var vlq = toVLQSigned(aValue);
+
+ do {
+ digit = vlq & VLQ_BASE_MASK;
+ vlq >>>= VLQ_BASE_SHIFT;
+ if (vlq > 0) {
+ // There are still more digits in this value, so we must make sure the
+ // continuation bit is marked.
+ digit |= VLQ_CONTINUATION_BIT;
+ }
+ encoded += base64.encode(digit);
+ } while (vlq > 0);
+
+ return encoded;
+ };
+
+ /**
+ * Decodes the next base 64 VLQ value from the given string and returns the
+ * value and the rest of the string.
+ */
+ exports.decode = function base64VLQ_decode(aStr) {
+ var i = 0;
+ var strLen = aStr.length;
+ var result = 0;
+ var shift = 0;
+ var continuation, digit;
+
+ do {
+ if (i >= strLen) {
+ throw new Error("Expected more digits in base 64 VLQ value.");
+ }
+ digit = base64.decode(aStr.charAt(i++));
+ continuation = !!(digit & VLQ_CONTINUATION_BIT);
+ digit &= VLQ_BASE_MASK;
+ result = result + (digit << shift);
+ shift += VLQ_BASE_SHIFT;
+ } while (continuation);
+
+ return {
+ value: fromVLQSigned(result),
+ rest: aStr.slice(i)
+ };
+ };
+
+});
+
+},{"./base64":8,"amdefine":14}],8:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var charToIntMap = {};
+ var intToCharMap = {};
+
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ .split('')
+ .forEach(function (ch, index) {
+ charToIntMap[ch] = index;
+ intToCharMap[index] = ch;
+ });
+
+ /**
+ * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+ */
+ exports.encode = function base64_encode(aNumber) {
+ if (aNumber in intToCharMap) {
+ return intToCharMap[aNumber];
+ }
+ throw new TypeError("Must be between 0 and 63: " + aNumber);
+ };
+
+ /**
+ * Decode a single base 64 digit to an integer.
+ */
+ exports.decode = function base64_decode(aChar) {
+ if (aChar in charToIntMap) {
+ return charToIntMap[aChar];
+ }
+ throw new TypeError("Not a valid base 64 digit: " + aChar);
+ };
+
+});
+
+},{"amdefine":14}],9:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ /**
+ * Recursive implementation of binary search.
+ *
+ * @param aLow Indices here and lower do not contain the needle.
+ * @param aHigh Indices here and higher do not contain the needle.
+ * @param aNeedle The element being searched for.
+ * @param aHaystack The non-empty array being searched.
+ * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+ */
+ function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
+ // This function terminates when one of the following is true:
+ //
+ // 1. We find the exact element we are looking for.
+ //
+ // 2. We did not find the exact element, but we can return the next
+ // closest element that is less than that element.
+ //
+ // 3. We did not find the exact element, and there is no next-closest
+ // element which is less than the one we are searching for, so we
+ // return null.
+ var mid = Math.floor((aHigh - aLow) / 2) + aLow;
+ var cmp = aCompare(aNeedle, aHaystack[mid]);
+ if (cmp === 0) {
+ // Found the element we are looking for.
+ return aHaystack[mid];
+ }
+ else if (cmp > 0) {
+ // aHaystack[mid] is greater than our needle.
+ if (aHigh - mid > 1) {
+ // The element is in the upper half.
+ return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
+ }
+ // We did not find an exact match, return the next closest one
+ // (termination case 2).
+ return aHaystack[mid];
+ }
+ else {
+ // aHaystack[mid] is less than our needle.
+ if (mid - aLow > 1) {
+ // The element is in the lower half.
+ return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
+ }
+ // The exact needle element was not found in this haystack. Determine if
+ // we are in termination case (2) or (3) and return the appropriate thing.
+ return aLow < 0
+ ? null
+ : aHaystack[aLow];
+ }
+ }
+
+ /**
+ * This is an implementation of binary search which will always try and return
+ * the next lowest value checked if there is no exact hit. This is because
+ * mappings between original and generated line/col pairs are single points,
+ * and there is an implicit region between each of them, so a miss just means
+ * that you aren't on the very start of a region.
+ *
+ * @param aNeedle The element you are looking for.
+ * @param aHaystack The array that is being searched.
+ * @param aCompare A function which takes the needle and an element in the
+ * array and returns -1, 0, or 1 depending on whether the needle is less
+ * than, equal to, or greater than the element, respectively.
+ */
+ exports.search = function search(aNeedle, aHaystack, aCompare) {
+ return aHaystack.length > 0
+ ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
+ : null;
+ };
+
+});
+
+},{"amdefine":14}],10:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var util = require('./util');
+ var binarySearch = require('./binary-search');
+ var ArraySet = require('./array-set').ArraySet;
+ var base64VLQ = require('./base64-vlq');
+
+ /**
+ * A SourceMapConsumer instance represents a parsed source map which we can
+ * query for information about the original file positions by giving it a file
+ * position in the generated source.
+ *
+ * The only parameter is the raw source map (either as a JSON string, or
+ * already parsed to an object). According to the spec, source maps have the
+ * following attributes:
+ *
+ * - version: Which version of the source map spec this map is following.
+ * - sources: An array of URLs to the original source files.
+ * - names: An array of identifiers which can be referrenced by individual mappings.
+ * - sourceRoot: Optional. The URL root from which all sources are relative.
+ * - sourcesContent: Optional. An array of contents of the original source files.
+ * - mappings: A string of base64 VLQs which contain the actual mappings.
+ * - file: The generated file this source map is associated with.
+ *
+ * Here is an example source map, taken from the source map spec[0]:
+ *
+ * {
+ * version : 3,
+ * file: "out.js",
+ * sourceRoot : "",
+ * sources: ["foo.js", "bar.js"],
+ * names: ["src", "maps", "are", "fun"],
+ * mappings: "AA,AB;;ABCDE;"
+ * }
+ *
+ * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+ */
+ function SourceMapConsumer(aSourceMap) {
+ var sourceMap = aSourceMap;
+ if (typeof aSourceMap === 'string') {
+ sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
+ }
+
+ var version = util.getArg(sourceMap, 'version');
+ var sources = util.getArg(sourceMap, 'sources');
+ var names = util.getArg(sourceMap, 'names');
+ var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
+ var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
+ var mappings = util.getArg(sourceMap, 'mappings');
+ var file = util.getArg(sourceMap, 'file');
+
+ if (version !== this._version) {
+ throw new Error('Unsupported version: ' + version);
+ }
+
+ this._names = ArraySet.fromArray(names);
+ this._sources = ArraySet.fromArray(sources);
+ this.sourceRoot = sourceRoot;
+ this.sourcesContent = sourcesContent;
+ this.file = file;
+
+ // `this._generatedMappings` and `this._originalMappings` hold the parsed
+ // mapping coordinates from the source map's "mappings" attribute. Each
+ // object in the array is of the form
+ //
+ // {
+ // generatedLine: The line number in the generated code,
+ // generatedColumn: The column number in the generated code,
+ // source: The path to the original source file that generated this
+ // chunk of code,
+ // originalLine: The line number in the original source that
+ // corresponds to this chunk of generated code,
+ // originalColumn: The column number in the original source that
+ // corresponds to this chunk of generated code,
+ // name: The name of the original symbol which generated this chunk of
+ // code.
+ // }
+ //
+ // All properties except for `generatedLine` and `generatedColumn` can be
+ // `null`.
+ //
+ // `this._generatedMappings` is ordered by the generated positions.
+ //
+ // `this._originalMappings` is ordered by the original positions.
+ this._generatedMappings = [];
+ this._originalMappings = [];
+ this._parseMappings(mappings, sourceRoot);
+ }
+
+ /**
+ * The version of the source mapping spec that we are consuming.
+ */
+ SourceMapConsumer.prototype._version = 3;
+
+ /**
+ * The list of original sources.
+ */
+ Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
+ get: function () {
+ return this._sources.toArray().map(function (s) {
+ return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
+ }, this);
+ }
+ });
+
+ /**
+ * Parse the mappings in a string in to a data structure which we can easily
+ * query (an ordered list in this._generatedMappings).
+ */
+ SourceMapConsumer.prototype._parseMappings =
+ function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
+ var generatedLine = 1;
+ var previousGeneratedColumn = 0;
+ var previousOriginalLine = 0;
+ var previousOriginalColumn = 0;
+ var previousSource = 0;
+ var previousName = 0;
+ var mappingSeparator = /^[,;]/;
+ var str = aStr;
+ var mapping;
+ var temp;
+
+ while (str.length > 0) {
+ if (str.charAt(0) === ';') {
+ generatedLine++;
+ str = str.slice(1);
+ previousGeneratedColumn = 0;
+ }
+ else if (str.charAt(0) === ',') {
+ str = str.slice(1);
+ }
+ else {
+ mapping = {};
+ mapping.generatedLine = generatedLine;
+
+ // Generated column.
+ temp = base64VLQ.decode(str);
+ mapping.generatedColumn = previousGeneratedColumn + temp.value;
+ previousGeneratedColumn = mapping.generatedColumn;
+ str = temp.rest;
+
+ if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+ // Original source.
+ temp = base64VLQ.decode(str);
+ mapping.source = this._sources.at(previousSource + temp.value);
+ previousSource += temp.value;
+ str = temp.rest;
+ if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+ throw new Error('Found a source, but no line and column');
+ }
+
+ // Original line.
+ temp = base64VLQ.decode(str);
+ mapping.originalLine = previousOriginalLine + temp.value;
+ previousOriginalLine = mapping.originalLine;
+ // Lines are stored 0-based
+ mapping.originalLine += 1;
+ str = temp.rest;
+ if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
+ throw new Error('Found a source and line, but no column');
+ }
+
+ // Original column.
+ temp = base64VLQ.decode(str);
+ mapping.originalColumn = previousOriginalColumn + temp.value;
+ previousOriginalColumn = mapping.originalColumn;
+ str = temp.rest;
+
+ if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
+ // Original name.
+ temp = base64VLQ.decode(str);
+ mapping.name = this._names.at(previousName + temp.value);
+ previousName += temp.value;
+ str = temp.rest;
+ }
+ }
+
+ this._generatedMappings.push(mapping);
+ if (typeof mapping.originalLine === 'number') {
+ this._originalMappings.push(mapping);
+ }
+ }
+ }
+
+ this._originalMappings.sort(this._compareOriginalPositions);
+ };
+
+ /**
+ * Comparator between two mappings where the original positions are compared.
+ */
+ SourceMapConsumer.prototype._compareOriginalPositions =
+ function SourceMapConsumer_compareOriginalPositions(mappingA, mappingB) {
+ if (mappingA.source > mappingB.source) {
+ return 1;
+ }
+ else if (mappingA.source < mappingB.source) {
+ return -1;
+ }
+ else {
+ var cmp = mappingA.originalLine - mappingB.originalLine;
+ return cmp === 0
+ ? mappingA.originalColumn - mappingB.originalColumn
+ : cmp;
+ }
+ };
+
+ /**
+ * Comparator between two mappings where the generated positions are compared.
+ */
+ SourceMapConsumer.prototype._compareGeneratedPositions =
+ function SourceMapConsumer_compareGeneratedPositions(mappingA, mappingB) {
+ var cmp = mappingA.generatedLine - mappingB.generatedLine;
+ return cmp === 0
+ ? mappingA.generatedColumn - mappingB.generatedColumn
+ : cmp;
+ };
+
+ /**
+ * Find the mapping that best matches the hypothetical "needle" mapping that
+ * we are searching for in the given "haystack" of mappings.
+ */
+ SourceMapConsumer.prototype._findMapping =
+ function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
+ aColumnName, aComparator) {
+ // To return the position we are searching for, we must first find the
+ // mapping for the given position and then return the opposite position it
+ // points to. Because the mappings are sorted, we can use binary search to
+ // find the best mapping.
+
+ if (aNeedle[aLineName] <= 0) {
+ throw new TypeError('Line must be greater than or equal to 1, got '
+ + aNeedle[aLineName]);
+ }
+ if (aNeedle[aColumnName] < 0) {
+ throw new TypeError('Column must be greater than or equal to 0, got '
+ + aNeedle[aColumnName]);
+ }
+
+ return binarySearch.search(aNeedle, aMappings, aComparator);
+ };
+
+ /**
+ * Returns the original source, line, and column information for the generated
+ * source's line and column positions provided. The only argument is an object
+ * with the following properties:
+ *
+ * - line: The line number in the generated source.
+ * - column: The column number in the generated source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - source: The original source file, or null.
+ * - line: The line number in the original source, or null.
+ * - column: The column number in the original source, or null.
+ * - name: The original identifier, or null.
+ */
+ SourceMapConsumer.prototype.originalPositionFor =
+ function SourceMapConsumer_originalPositionFor(aArgs) {
+ var needle = {
+ generatedLine: util.getArg(aArgs, 'line'),
+ generatedColumn: util.getArg(aArgs, 'column')
+ };
+
+ var mapping = this._findMapping(needle,
+ this._generatedMappings,
+ "generatedLine",
+ "generatedColumn",
+ this._compareGeneratedPositions);
+
+ if (mapping) {
+ var source = util.getArg(mapping, 'source', null);
+ if (source && this.sourceRoot) {
+ source = util.join(this.sourceRoot, source);
+ }
+ return {
+ source: source,
+ line: util.getArg(mapping, 'originalLine', null),
+ column: util.getArg(mapping, 'originalColumn', null),
+ name: util.getArg(mapping, 'name', null)
+ };
+ }
+
+ return {
+ source: null,
+ line: null,
+ column: null,
+ name: null
+ };
+ };
+
+ /**
+ * Returns the original source content. The only argument is the url of the
+ * original source file. Returns null if no original source content is
+ * availible.
+ */
+ SourceMapConsumer.prototype.sourceContentFor =
+ function SourceMapConsumer_sourceContentFor(aSource) {
+ if (!this.sourcesContent) {
+ return null;
+ }
+
+ if (this.sourceRoot) {
+ aSource = util.relative(this.sourceRoot, aSource);
+ }
+
+ if (this._sources.has(aSource)) {
+ return this.sourcesContent[this._sources.indexOf(aSource)];
+ }
+
+ var url;
+ if (this.sourceRoot
+ && (url = util.urlParse(this.sourceRoot))) {
+ // XXX: file:// URIs and absolute paths lead to unexpected behavior for
+ // many users. We can help them out when they expect file:// URIs to
+ // behave like it would if they were running a local HTTP server. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
+ var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
+ if (url.scheme == "file"
+ && this._sources.has(fileUriAbsPath)) {
+ return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
+ }
+
+ if ((!url.path || url.path == "/")
+ && this._sources.has("/" + aSource)) {
+ return this.sourcesContent[this._sources.indexOf("/" + aSource)];
+ }
+ }
+
+ throw new Error('"' + aSource + '" is not in the SourceMap.');
+ };
+
+ /**
+ * Returns the generated line and column information for the original source,
+ * line, and column positions provided. The only argument is an object with
+ * the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source.
+ * - column: The column number in the original source.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - line: The line number in the generated source, or null.
+ * - column: The column number in the generated source, or null.
+ */
+ SourceMapConsumer.prototype.generatedPositionFor =
+ function SourceMapConsumer_generatedPositionFor(aArgs) {
+ var needle = {
+ source: util.getArg(aArgs, 'source'),
+ originalLine: util.getArg(aArgs, 'line'),
+ originalColumn: util.getArg(aArgs, 'column')
+ };
+
+ if (this.sourceRoot) {
+ needle.source = util.relative(this.sourceRoot, needle.source);
+ }
+
+ var mapping = this._findMapping(needle,
+ this._originalMappings,
+ "originalLine",
+ "originalColumn",
+ this._compareOriginalPositions);
+
+ if (mapping) {
+ return {
+ line: util.getArg(mapping, 'generatedLine', null),
+ column: util.getArg(mapping, 'generatedColumn', null)
+ };
+ }
+
+ return {
+ line: null,
+ column: null
+ };
+ };
+
+ SourceMapConsumer.GENERATED_ORDER = 1;
+ SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+ /**
+ * Iterate over each mapping between an original source/line/column and a
+ * generated line/column in this source map.
+ *
+ * @param Function aCallback
+ * The function that is called with each mapping.
+ * @param Object aContext
+ * Optional. If specified, this object will be the value of `this` every
+ * time that `aCallback` is called.
+ * @param aOrder
+ * Either `SourceMapConsumer.GENERATED_ORDER` or
+ * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+ * iterate over the mappings sorted by the generated file's line/column
+ * order or the original's source/line/column order, respectively. Defaults to
+ * `SourceMapConsumer.GENERATED_ORDER`.
+ */
+ SourceMapConsumer.prototype.eachMapping =
+ function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
+ var context = aContext || null;
+ var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+ var mappings;
+ switch (order) {
+ case SourceMapConsumer.GENERATED_ORDER:
+ mappings = this._generatedMappings;
+ break;
+ case SourceMapConsumer.ORIGINAL_ORDER:
+ mappings = this._originalMappings;
+ break;
+ default:
+ throw new Error("Unknown order of iteration.");
+ }
+
+ var sourceRoot = this.sourceRoot;
+ mappings.map(function (mapping) {
+ var source = mapping.source;
+ if (source && sourceRoot) {
+ source = util.join(sourceRoot, source);
+ }
+ return {
+ source: source,
+ generatedLine: mapping.generatedLine,
+ generatedColumn: mapping.generatedColumn,
+ originalLine: mapping.originalLine,
+ originalColumn: mapping.originalColumn,
+ name: mapping.name
+ };
+ }).forEach(aCallback, context);
+ };
+
+ exports.SourceMapConsumer = SourceMapConsumer;
+
+});
+
+},{"./array-set":6,"./base64-vlq":7,"./binary-search":9,"./util":13,"amdefine":14}],11:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var base64VLQ = require('./base64-vlq');
+ var util = require('./util');
+ var ArraySet = require('./array-set').ArraySet;
+
+ /**
+ * An instance of the SourceMapGenerator represents a source map which is
+ * being built incrementally. To create a new one, you must pass an object
+ * with the following properties:
+ *
+ * - file: The filename of the generated source.
+ * - sourceRoot: An optional root for all URLs in this source map.
+ */
+ function SourceMapGenerator(aArgs) {
+ this._file = util.getArg(aArgs, 'file');
+ this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
+ this._sources = new ArraySet();
+ this._names = new ArraySet();
+ this._mappings = [];
+ this._sourcesContents = null;
+ }
+
+ SourceMapGenerator.prototype._version = 3;
+
+ /**
+ * Creates a new SourceMapGenerator based on a SourceMapConsumer
+ *
+ * @param aSourceMapConsumer The SourceMap.
+ */
+ SourceMapGenerator.fromSourceMap =
+ function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
+ var sourceRoot = aSourceMapConsumer.sourceRoot;
+ var generator = new SourceMapGenerator({
+ file: aSourceMapConsumer.file,
+ sourceRoot: sourceRoot
+ });
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ var newMapping = {
+ generated: {
+ line: mapping.generatedLine,
+ column: mapping.generatedColumn
+ }
+ };
+
+ if (mapping.source) {
+ newMapping.source = mapping.source;
+ if (sourceRoot) {
+ newMapping.source = util.relative(sourceRoot, newMapping.source);
+ }
+
+ newMapping.original = {
+ line: mapping.originalLine,
+ column: mapping.originalColumn
+ };
+
+ if (mapping.name) {
+ newMapping.name = mapping.name;
+ }
+ }
+
+ generator.addMapping(newMapping);
+ });
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content) {
+ generator.setSourceContent(sourceFile, content);
+ }
+ });
+ return generator;
+ };
+
+ /**
+ * Add a single mapping from original source line and column to the generated
+ * source's line and column for this source map being created. The mapping
+ * object should have the following properties:
+ *
+ * - generated: An object with the generated line and column positions.
+ * - original: An object with the original line and column positions.
+ * - source: The original source file (relative to the sourceRoot).
+ * - name: An optional original token name for this mapping.
+ */
+ SourceMapGenerator.prototype.addMapping =
+ function SourceMapGenerator_addMapping(aArgs) {
+ var generated = util.getArg(aArgs, 'generated');
+ var original = util.getArg(aArgs, 'original', null);
+ var source = util.getArg(aArgs, 'source', null);
+ var name = util.getArg(aArgs, 'name', null);
+
+ this._validateMapping(generated, original, source, name);
+
+ if (source && !this._sources.has(source)) {
+ this._sources.add(source);
+ }
+
+ if (name && !this._names.has(name)) {
+ this._names.add(name);
+ }
+
+ this._mappings.push({
+ generated: generated,
+ original: original,
+ source: source,
+ name: name
+ });
+ };
+
+ /**
+ * Set the source content for a source file.
+ */
+ SourceMapGenerator.prototype.setSourceContent =
+ function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
+ var source = aSourceFile;
+ if (this._sourceRoot) {
+ source = util.relative(this._sourceRoot, source);
+ }
+
+ if (aSourceContent !== null) {
+ // Add the source content to the _sourcesContents map.
+ // Create a new _sourcesContents map if the property is null.
+ if (!this._sourcesContents) {
+ this._sourcesContents = {};
+ }
+ this._sourcesContents[util.toSetString(source)] = aSourceContent;
+ } else {
+ // Remove the source file from the _sourcesContents map.
+ // If the _sourcesContents map is empty, set the property to null.
+ delete this._sourcesContents[util.toSetString(source)];
+ if (Object.keys(this._sourcesContents).length === 0) {
+ this._sourcesContents = null;
+ }
+ }
+ };
+
+ /**
+ * Applies the mappings of a sub-source-map for a specific source file to the
+ * source map being generated. Each mapping to the supplied source file is
+ * rewritten using the supplied source map. Note: The resolution for the
+ * resulting mappings is the minimium of this map and the supplied map.
+ *
+ * @param aSourceMapConsumer The source map to be applied.
+ * @param aSourceFile Optional. The filename of the source file.
+ * If omitted, SourceMapConsumer's file property will be used.
+ */
+ SourceMapGenerator.prototype.applySourceMap =
+ function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
+ // If aSourceFile is omitted, we will use the file property of the SourceMap
+ if (!aSourceFile) {
+ aSourceFile = aSourceMapConsumer.file;
+ }
+ var sourceRoot = this._sourceRoot;
+ // Make "aSourceFile" relative if an absolute Url is passed.
+ if (sourceRoot) {
+ aSourceFile = util.relative(sourceRoot, aSourceFile);
+ }
+ // Applying the SourceMap can add and remove items from the sources and
+ // the names array.
+ var newSources = new ArraySet();
+ var newNames = new ArraySet();
+
+ // Find mappings for the "aSourceFile"
+ this._mappings.forEach(function (mapping) {
+ if (mapping.source === aSourceFile && mapping.original) {
+ // Check if it can be mapped by the source map, then update the mapping.
+ var original = aSourceMapConsumer.originalPositionFor({
+ line: mapping.original.line,
+ column: mapping.original.column
+ });
+ if (original.source !== null) {
+ // Copy mapping
+ if (sourceRoot) {
+ mapping.source = util.relative(sourceRoot, original.source);
+ } else {
+ mapping.source = original.source;
+ }
+ mapping.original.line = original.line;
+ mapping.original.column = original.column;
+ if (original.name !== null && mapping.name !== null) {
+ // Only use the identifier name if it's an identifier
+ // in both SourceMaps
+ mapping.name = original.name;
+ }
+ }
+ }
+
+ var source = mapping.source;
+ if (source && !newSources.has(source)) {
+ newSources.add(source);
+ }
+
+ var name = mapping.name;
+ if (name && !newNames.has(name)) {
+ newNames.add(name);
+ }
+
+ }, this);
+ this._sources = newSources;
+ this._names = newNames;
+
+ // Copy sourcesContents of applied map.
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content) {
+ if (sourceRoot) {
+ sourceFile = util.relative(sourceRoot, sourceFile);
+ }
+ this.setSourceContent(sourceFile, content);
+ }
+ }, this);
+ };
+
+ /**
+ * A mapping can have one of the three levels of data:
+ *
+ * 1. Just the generated position.
+ * 2. The Generated position, original position, and original source.
+ * 3. Generated and original position, original source, as well as a name
+ * token.
+ *
+ * To maintain consistency, we validate that any new mapping being added falls
+ * in to one of these categories.
+ */
+ SourceMapGenerator.prototype._validateMapping =
+ function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
+ aName) {
+ if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+ && aGenerated.line > 0 && aGenerated.column >= 0
+ && !aOriginal && !aSource && !aName) {
+ // Case 1.
+ return;
+ }
+ else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
+ && aOriginal && 'line' in aOriginal && 'column' in aOriginal
+ && aGenerated.line > 0 && aGenerated.column >= 0
+ && aOriginal.line > 0 && aOriginal.column >= 0
+ && aSource) {
+ // Cases 2 and 3.
+ return;
+ }
+ else {
+ throw new Error('Invalid mapping.');
+ }
+ };
+
+ function cmpLocation(loc1, loc2) {
+ var cmp = (loc1 && loc1.line) - (loc2 && loc2.line);
+ return cmp ? cmp : (loc1 && loc1.column) - (loc2 && loc2.column);
+ }
+
+ function strcmp(str1, str2) {
+ str1 = str1 || '';
+ str2 = str2 || '';
+ return (str1 > str2) - (str1 < str2);
+ }
+
+ function cmpMapping(mappingA, mappingB) {
+ return cmpLocation(mappingA.generated, mappingB.generated) ||
+ cmpLocation(mappingA.original, mappingB.original) ||
+ strcmp(mappingA.source, mappingB.source) ||
+ strcmp(mappingA.name, mappingB.name);
+ }
+
+ /**
+ * Serialize the accumulated mappings in to the stream of base 64 VLQs
+ * specified by the source map format.
+ */
+ SourceMapGenerator.prototype._serializeMappings =
+ function SourceMapGenerator_serializeMappings() {
+ var previousGeneratedColumn = 0;
+ var previousGeneratedLine = 1;
+ var previousOriginalColumn = 0;
+ var previousOriginalLine = 0;
+ var previousName = 0;
+ var previousSource = 0;
+ var result = '';
+ var mapping;
+
+ // The mappings must be guaranteed to be in sorted order before we start
+ // serializing them or else the generated line numbers (which are defined
+ // via the ';' separators) will be all messed up. Note: it might be more
+ // performant to maintain the sorting as we insert them, rather than as we
+ // serialize them, but the big O is the same either way.
+ this._mappings.sort(cmpMapping);
+
+ for (var i = 0, len = this._mappings.length; i < len; i++) {
+ mapping = this._mappings[i];
+
+ if (mapping.generated.line !== previousGeneratedLine) {
+ previousGeneratedColumn = 0;
+ while (mapping.generated.line !== previousGeneratedLine) {
+ result += ';';
+ previousGeneratedLine++;
+ }
+ }
+ else {
+ if (i > 0) {
+ if (!cmpMapping(mapping, this._mappings[i - 1])) {
+ continue;
+ }
+ result += ',';
+ }
+ }
+
+ result += base64VLQ.encode(mapping.generated.column
+ - previousGeneratedColumn);
+ previousGeneratedColumn = mapping.generated.column;
+
+ if (mapping.source && mapping.original) {
+ result += base64VLQ.encode(this._sources.indexOf(mapping.source)
+ - previousSource);
+ previousSource = this._sources.indexOf(mapping.source);
+
+ // lines are stored 0-based in SourceMap spec version 3
+ result += base64VLQ.encode(mapping.original.line - 1
+ - previousOriginalLine);
+ previousOriginalLine = mapping.original.line - 1;
+
+ result += base64VLQ.encode(mapping.original.column
+ - previousOriginalColumn);
+ previousOriginalColumn = mapping.original.column;
+
+ if (mapping.name) {
+ result += base64VLQ.encode(this._names.indexOf(mapping.name)
+ - previousName);
+ previousName = this._names.indexOf(mapping.name);
+ }
+ }
+ }
+
+ return result;
+ };
+
+ /**
+ * Externalize the source map.
+ */
+ SourceMapGenerator.prototype.toJSON =
+ function SourceMapGenerator_toJSON() {
+ var map = {
+ version: this._version,
+ file: this._file,
+ sources: this._sources.toArray(),
+ names: this._names.toArray(),
+ mappings: this._serializeMappings()
+ };
+ if (this._sourceRoot) {
+ map.sourceRoot = this._sourceRoot;
+ }
+ if (this._sourcesContents) {
+ map.sourcesContent = map.sources.map(function (source) {
+ if (map.sourceRoot) {
+ source = util.relative(map.sourceRoot, source);
+ }
+ return Object.prototype.hasOwnProperty.call(
+ this._sourcesContents, util.toSetString(source))
+ ? this._sourcesContents[util.toSetString(source)]
+ : null;
+ }, this);
+ }
+ return map;
+ };
+
+ /**
+ * Render the source map being generated to a string.
+ */
+ SourceMapGenerator.prototype.toString =
+ function SourceMapGenerator_toString() {
+ return JSON.stringify(this);
+ };
+
+ exports.SourceMapGenerator = SourceMapGenerator;
+
+});
+
+},{"./array-set":6,"./base64-vlq":7,"./util":13,"amdefine":14}],12:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
+ var util = require('./util');
+
+ /**
+ * SourceNodes provide a way to abstract over interpolating/concatenating
+ * snippets of generated JavaScript source code while maintaining the line and
+ * column information associated with the original source code.
+ *
+ * @param aLine The original line number.
+ * @param aColumn The original column number.
+ * @param aSource The original source's filename.
+ * @param aChunks Optional. An array of strings which are snippets of
+ * generated JS, or other SourceNodes.
+ * @param aName The original identifier.
+ */
+ function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
+ this.children = [];
+ this.sourceContents = {};
+ this.line = aLine === undefined ? null : aLine;
+ this.column = aColumn === undefined ? null : aColumn;
+ this.source = aSource === undefined ? null : aSource;
+ this.name = aName === undefined ? null : aName;
+ if (aChunks != null) this.add(aChunks);
+ }
+
+ /**
+ * Creates a SourceNode from generated code and a SourceMapConsumer.
+ *
+ * @param aGeneratedCode The generated code
+ * @param aSourceMapConsumer The SourceMap for the generated code
+ */
+ SourceNode.fromStringWithSourceMap =
+ function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
+ // The SourceNode we want to fill with the generated code
+ // and the SourceMap
+ var node = new SourceNode();
+
+ // The generated code
+ // Processed fragments are removed from this array.
+ var remainingLines = aGeneratedCode.split('\n');
+
+ // We need to remember the position of "remainingLines"
+ var lastGeneratedLine = 1, lastGeneratedColumn = 0;
+
+ // The generate SourceNodes we need a code range.
+ // To extract it current and last mapping is used.
+ // Here we store the last mapping.
+ var lastMapping = null;
+
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ if (lastMapping === null) {
+ // We add the generated code until the first mapping
+ // to the SourceNode without any mapping.
+ // Each line is added as separate string.
+ while (lastGeneratedLine < mapping.generatedLine) {
+ node.add(remainingLines.shift() + "\n");
+ lastGeneratedLine++;
+ }
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ var nextLine = remainingLines[0];
+ node.add(nextLine.substr(0, mapping.generatedColumn));
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
+ } else {
+ // We add the code from "lastMapping" to "mapping":
+ // First check if there is a new line in between.
+ if (lastGeneratedLine < mapping.generatedLine) {
+ var code = "";
+ // Associate full lines with "lastMapping"
+ do {
+ code += remainingLines.shift() + "\n";
+ lastGeneratedLine++;
+ lastGeneratedColumn = 0;
+ } while (lastGeneratedLine < mapping.generatedLine);
+ // When we reached the correct line, we add code until we
+ // reach the correct column too.
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ var nextLine = remainingLines[0];
+ code += nextLine.substr(0, mapping.generatedColumn);
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
+ // Create the SourceNode.
+ addMappingWithCode(lastMapping, code);
+ } else {
+ // There is no new line in between.
+ // Associate the code between "lastGeneratedColumn" and
+ // "mapping.generatedColumn" with "lastMapping"
+ var nextLine = remainingLines[0];
+ var code = nextLine.substr(0, mapping.generatedColumn -
+ lastGeneratedColumn);
+ remainingLines[0] = nextLine.substr(mapping.generatedColumn -
+ lastGeneratedColumn);
+ lastGeneratedColumn = mapping.generatedColumn;
+ addMappingWithCode(lastMapping, code);
+ }
+ }
+ lastMapping = mapping;
+ }, this);
+ // We have processed all mappings.
+ // Associate the remaining code in the current line with "lastMapping"
+ // and add the remaining lines without any mapping
+ addMappingWithCode(lastMapping, remainingLines.join("\n"));
+
+ // Copy sourcesContent into SourceNode
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ var content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content) {
+ node.setSourceContent(sourceFile, content);
+ }
+ });
+
+ return node;
+
+ function addMappingWithCode(mapping, code) {
+ if (mapping === null || mapping.source === undefined) {
+ node.add(code);
+ } else {
+ node.add(new SourceNode(mapping.originalLine,
+ mapping.originalColumn,
+ mapping.source,
+ code,
+ mapping.name));
+ }
+ }
+ };
+
+ /**
+ * Add a chunk of generated JS to this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ SourceNode.prototype.add = function SourceNode_add(aChunk) {
+ if (Array.isArray(aChunk)) {
+ aChunk.forEach(function (chunk) {
+ this.add(chunk);
+ }, this);
+ }
+ else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+ if (aChunk) {
+ this.children.push(aChunk);
+ }
+ }
+ else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+ );
+ }
+ return this;
+ };
+
+ /**
+ * Add a chunk of generated JS to the beginning of this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
+ if (Array.isArray(aChunk)) {
+ for (var i = aChunk.length-1; i >= 0; i--) {
+ this.prepend(aChunk[i]);
+ }
+ }
+ else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
+ this.children.unshift(aChunk);
+ }
+ else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
+ );
+ }
+ return this;
+ };
+
+ /**
+ * Walk over the tree of JS snippets in this node and its children. The
+ * walking function is called once for each snippet of JS and is passed that
+ * snippet and the its original associated source's line/column location.
+ *
+ * @param aFn The traversal function.
+ */
+ SourceNode.prototype.walk = function SourceNode_walk(aFn) {
+ this.children.forEach(function (chunk) {
+ if (chunk instanceof SourceNode) {
+ chunk.walk(aFn);
+ }
+ else {
+ if (chunk !== '') {
+ aFn(chunk, { source: this.source,
+ line: this.line,
+ column: this.column,
+ name: this.name });
+ }
+ }
+ }, this);
+ };
+
+ /**
+ * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+ * each of `this.children`.
+ *
+ * @param aSep The separator.
+ */
+ SourceNode.prototype.join = function SourceNode_join(aSep) {
+ var newChildren;
+ var i;
+ var len = this.children.length;
+ if (len > 0) {
+ newChildren = [];
+ for (i = 0; i < len-1; i++) {
+ newChildren.push(this.children[i]);
+ newChildren.push(aSep);
+ }
+ newChildren.push(this.children[i]);
+ this.children = newChildren;
+ }
+ return this;
+ };
+
+ /**
+ * Call String.prototype.replace on the very right-most source snippet. Useful
+ * for trimming whitespace from the end of a source node, etc.
+ *
+ * @param aPattern The pattern to replace.
+ * @param aReplacement The thing to replace the pattern with.
+ */
+ SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
+ var lastChild = this.children[this.children.length - 1];
+ if (lastChild instanceof SourceNode) {
+ lastChild.replaceRight(aPattern, aReplacement);
+ }
+ else if (typeof lastChild === 'string') {
+ this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
+ }
+ else {
+ this.children.push(''.replace(aPattern, aReplacement));
+ }
+ return this;
+ };
+
+ /**
+ * Set the source content for a source file. This will be added to the SourceMapGenerator
+ * in the sourcesContent field.
+ *
+ * @param aSourceFile The filename of the source file
+ * @param aSourceContent The content of the source file
+ */
+ SourceNode.prototype.setSourceContent =
+ function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
+ this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+ };
+
+ /**
+ * Walk over the tree of SourceNodes. The walking function is called for each
+ * source file content and is passed the filename and source content.
+ *
+ * @param aFn The traversal function.
+ */
+ SourceNode.prototype.walkSourceContents =
+ function SourceNode_walkSourceContents(aFn) {
+ this.children.forEach(function (chunk) {
+ if (chunk instanceof SourceNode) {
+ chunk.walkSourceContents(aFn);
+ }
+ }, this);
+ Object.keys(this.sourceContents).forEach(function (sourceFileKey) {
+ aFn(util.fromSetString(sourceFileKey), this.sourceContents[sourceFileKey]);
+ }, this);
+ };
+
+ /**
+ * Return the string representation of this source node. Walks over the tree
+ * and concatenates all the various snippets together to one string.
+ */
+ SourceNode.prototype.toString = function SourceNode_toString() {
+ var str = "";
+ this.walk(function (chunk) {
+ str += chunk;
+ });
+ return str;
+ };
+
+ /**
+ * Returns the string representation of this source node along with a source
+ * map.
+ */
+ SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
+ var generated = {
+ code: "",
+ line: 1,
+ column: 0
+ };
+ var map = new SourceMapGenerator(aArgs);
+ var sourceMappingActive = false;
+ this.walk(function (chunk, original) {
+ generated.code += chunk;
+ if (original.source !== null
+ && original.line !== null
+ && original.column !== null) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column
+ },
+ name: original.name
+ });
+ sourceMappingActive = true;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ generated: {
+ line: generated.line,
+ column: generated.column
+ }
+ });
+ sourceMappingActive = false;
+ }
+ chunk.split('').forEach(function (ch) {
+ if (ch === '\n') {
+ generated.line++;
+ generated.column = 0;
+ } else {
+ generated.column++;
+ }
+ });
+ });
+ this.walkSourceContents(function (sourceFile, sourceContent) {
+ map.setSourceContent(sourceFile, sourceContent);
+ });
+
+ return { code: generated.code, map: map };
+ };
+
+ exports.SourceNode = SourceNode;
+
+});
+
+},{"./source-map-generator":11,"./util":13,"amdefine":14}],13:[function(require,module,exports){
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+if (typeof define !== 'function') {
+ var define = require('amdefine')(module, require);
+}
+define(function (require, exports, module) {
+
+ /**
+ * This is a helper function for getting values from parameter/options
+ * objects.
+ *
+ * @param args The object we are extracting values from
+ * @param name The name of the property we are getting.
+ * @param defaultValue An optional value to return if the property is missing
+ * from the object. If this is not specified and the property is missing, an
+ * error will be thrown.
+ */
+ function getArg(aArgs, aName, aDefaultValue) {
+ if (aName in aArgs) {
+ return aArgs[aName];
+ } else if (arguments.length === 3) {
+ return aDefaultValue;
+ } else {
+ throw new Error('"' + aName + '" is a required argument.');
+ }
+ }
+ exports.getArg = getArg;
+
+ var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
+
+ function urlParse(aUrl) {
+ var match = aUrl.match(urlRegexp);
+ if (!match) {
+ return null;
+ }
+ return {
+ scheme: match[1],
+ auth: match[3],
+ host: match[4],
+ port: match[6],
+ path: match[7]
+ };
+ }
+ exports.urlParse = urlParse;
+
+ function urlGenerate(aParsedUrl) {
+ var url = aParsedUrl.scheme + "://";
+ if (aParsedUrl.auth) {
+ url += aParsedUrl.auth + "@"
+ }
+ if (aParsedUrl.host) {
+ url += aParsedUrl.host;
+ }
+ if (aParsedUrl.port) {
+ url += ":" + aParsedUrl.port
+ }
+ if (aParsedUrl.path) {
+ url += aParsedUrl.path;
+ }
+ return url;
+ }
+ exports.urlGenerate = urlGenerate;
+
+ function join(aRoot, aPath) {
+ var url;
+
+ if (aPath.match(urlRegexp)) {
+ return aPath;
+ }
+
+ if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
+ url.path = aPath;
+ return urlGenerate(url);
+ }
+
+ return aRoot.replace(/\/$/, '') + '/' + aPath;
+ }
+ exports.join = join;
+
+ /**
+ * Because behavior goes wacky when you set `__proto__` on objects, we
+ * have to prefix all the strings in our set with an arbitrary character.
+ *
+ * See https://github.com/mozilla/source-map/pull/31 and
+ * https://github.com/mozilla/source-map/issues/30
+ *
+ * @param String aStr
+ */
+ function toSetString(aStr) {
+ return '$' + aStr;
+ }
+ exports.toSetString = toSetString;
+
+ function fromSetString(aStr) {
+ return aStr.substr(1);
+ }
+ exports.fromSetString = fromSetString;
+
+ function relative(aRoot, aPath) {
+ aRoot = aRoot.replace(/\/$/, '');
+
+ var url = urlParse(aRoot);
+ if (aPath.charAt(0) == "/" && url && url.path == "/") {
+ return aPath.slice(1);
+ }
+
+ return aPath.indexOf(aRoot + '/') === 0
+ ? aPath.substr(aRoot.length + 1)
+ : aPath;
+ }
+ exports.relative = relative;
+
+});
+
+},{"amdefine":14}],14:[function(require,module,exports){
+(function(process,__filename){/** vim: et:ts=4:sw=4:sts=4
+ * @license amdefine 0.0.5 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/amdefine for details
+ */
+
+/*jslint node: true */
+/*global module, process */
+'use strict';
+
+var path = require('path');
+
+/**
+ * Creates a define for node.
+ * @param {Object} module the "module" object that is defined by Node for the
+ * current module.
+ * @param {Function} [require]. Node's require function for the current module.
+ * It only needs to be passed in Node versions before 0.5, when module.require
+ * did not exist.
+ * @returns {Function} a define function that is usable for the current node
+ * module.
+ */
+function amdefine(module, require) {
+ var defineCache = {},
+ loaderCache = {},
+ alreadyCalled = false,
+ makeRequire, stringRequire;
+
+ /**
+ * Trims the . and .. from an array of path segments.
+ * It will keep a leading path segment if a .. will become
+ * the first path segment, to help with module name lookups,
+ * which act like paths, but can be remapped. But the end result,
+ * all paths that use this function should look normalized.
+ * NOTE: this method MODIFIES the input array.
+ * @param {Array} ary the array of path segments.
+ */
+ function trimDots(ary) {
+ var i, part;
+ for (i = 0; ary[i]; i+= 1) {
+ part = ary[i];
+ if (part === '.') {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === '..') {
+ if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } else if (i > 0) {
+ ary.splice(i - 1, 2);
+ i -= 2;
+ }
+ }
+ }
+ }
+
+ function normalize(name, baseName) {
+ var baseParts;
+
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === '.') {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ baseParts = baseName.split('/');
+ baseParts = baseParts.slice(0, baseParts.length - 1);
+ baseParts = baseParts.concat(name.split('/'));
+ trimDots(baseParts);
+ name = baseParts.join('/');
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Create the normalize() function passed to a loader plugin's
+ * normalize method.
+ */
+ function makeNormalize(relName) {
+ return function (name) {
+ return normalize(name, relName);
+ };
+ }
+
+ function makeLoad(id) {
+ function load(value) {
+ loaderCache[id] = value;
+ }
+
+ load.fromText = function (id, text) {
+ //This one is difficult because the text can/probably uses
+ //define, and any relative paths and requires should be relative
+ //to that id was it would be found on disk. But this would require
+ //bootstrapping a module/require fairly deeply from node core.
+ //Not sure how best to go about that yet.
+ throw new Error('amdefine does not implement load.fromText');
+ };
+
+ return load;
+ }
+
+ makeRequire = function (systemRequire, exports, module, relId) {
+ function amdRequire(deps, callback) {
+ if (typeof deps === 'string') {
+ //Synchronous, single module require('')
+ return stringRequire(systemRequire, exports, module, deps, relId);
+ } else {
+ //Array of dependencies with a callback.
+
+ //Convert the dependencies to modules.
+ deps = deps.map(function (depName) {
+ return stringRequire(systemRequire, exports, module, depName, relId);
+ });
+
+ //Wait for next tick to call back the require call.
+ process.nextTick(function () {
+ callback.apply(null, deps);
+ });
+ }
+ }
+
+ amdRequire.toUrl = function (filePath) {
+ if (filePath.indexOf('.') === 0) {
+ return normalize(filePath, path.dirname(module.filename));
+ } else {
+ return filePath;
+ }
+ };
+
+ return amdRequire;
+ };
+
+ //Favor explicit value, passed in if the module wants to support Node 0.4.
+ require = require || function req() {
+ return module.require.apply(module, arguments);
+ };
+
+ function runFactory(id, deps, factory) {
+ var r, e, m, result;
+
+ if (id) {
+ e = loaderCache[id] = {};
+ m = {
+ id: id,
+ uri: __filename,
+ exports: e
+ };
+ r = makeRequire(require, e, m, id);
+ } else {
+ //Only support one define call per file
+ if (alreadyCalled) {
+ throw new Error('amdefine with no module ID cannot be called more than once per file.');
+ }
+ alreadyCalled = true;
+
+ //Use the real variables from node
+ //Use module.exports for exports, since
+ //the exports in here is amdefine exports.
+ e = module.exports;
+ m = module;
+ r = makeRequire(require, e, m, module.id);
+ }
+
+ //If there are dependencies, they are strings, so need
+ //to convert them to dependency values.
+ if (deps) {
+ deps = deps.map(function (depName) {
+ return r(depName);
+ });
+ }
+
+ //Call the factory with the right dependencies.
+ if (typeof factory === 'function') {
+ result = factory.apply(module.exports, deps);
+ } else {
+ result = factory;
+ }
+
+ if (result !== undefined) {
+ m.exports = result;
+ if (id) {
+ loaderCache[id] = m.exports;
+ }
+ }
+ }
+
+ stringRequire = function (systemRequire, exports, module, id, relId) {
+ //Split the ID by a ! so that
+ var index = id.indexOf('!'),
+ originalId = id,
+ prefix, plugin;
+
+ if (index === -1) {
+ id = normalize(id, relId);
+
+ //Straight module lookup. If it is one of the special dependencies,
+ //deal with it, otherwise, delegate to node.
+ if (id === 'require') {
+ return makeRequire(systemRequire, exports, module, relId);
+ } else if (id === 'exports') {
+ return exports;
+ } else if (id === 'module') {
+ return module;
+ } else if (loaderCache.hasOwnProperty(id)) {
+ return loaderCache[id];
+ } else if (defineCache[id]) {
+ runFactory.apply(null, defineCache[id]);
+ return loaderCache[id];
+ } else {
+ if(systemRequire) {
+ return systemRequire(originalId);
+ } else {
+ throw new Error('No module with ID: ' + id);
+ }
+ }
+ } else {
+ //There is a plugin in play.
+ prefix = id.substring(0, index);
+ id = id.substring(index + 1, id.length);
+
+ plugin = stringRequire(systemRequire, exports, module, prefix, relId);
+
+ if (plugin.normalize) {
+ id = plugin.normalize(id, makeNormalize(relId));
+ } else {
+ //Normalize the ID normally.
+ id = normalize(id, relId);
+ }
+
+ if (loaderCache[id]) {
+ return loaderCache[id];
+ } else {
+ plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
+
+ return loaderCache[id];
+ }
+ }
+ };
+
+ //Create a define function specific to the module asking for amdefine.
+ function define(id, deps, factory) {
+ if (Array.isArray(id)) {
+ factory = deps;
+ deps = id;
+ id = undefined;
+ } else if (typeof id !== 'string') {
+ factory = id;
+ id = deps = undefined;
+ }
+
+ if (deps && !Array.isArray(deps)) {
+ factory = deps;
+ deps = undefined;
+ }
+
+ if (!deps) {
+ deps = ['require', 'exports', 'module'];
+ }
+
+ //Set up properties for this module. If an ID, then use
+ //internal cache. If no ID, then use the external variables
+ //for this node module.
+ if (id) {
+ //Put the module in deep freeze until there is a
+ //require call for it.
+ defineCache[id] = [id, deps, factory];
+ } else {
+ runFactory(id, deps, factory);
+ }
+ }
+
+ //define.require, which has access to all the values in the
+ //cache. Useful for AMD modules that all have IDs in the file,
+ //but need to finally export a value to node based on one of those
+ //IDs.
+ define.require = function (id) {
+ if (loaderCache[id]) {
+ return loaderCache[id];
+ }
+
+ if (defineCache[id]) {
+ runFactory.apply(null, defineCache[id]);
+ return loaderCache[id];
+ }
+ };
+
+ define.amd = {};
+
+ return define;
+}
+
+module.exports = amdefine;
+
+})(require("__browserify_process"),"/../node_modules/source-map/node_modules/amdefine/amdefine.js")
+},{"__browserify_process":3,"path":2}],15:[function(require,module,exports){
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* jshint browser: true */
+/* jslint evil: true */
+
+'use strict';
+var runScripts;
+
+var transform = require('./fbtransform/lib/transform').transform;
+var visitors = require('./fbtransform/visitors').transformVisitors;
+var transform = transform.bind(null, visitors.react);
+var docblock = require('./fbtransform/lib/docblock');
+
+var headEl = document.getElementsByTagName('head')[0];
+
+exports.transform = transform;
+
+exports.exec = function(code) {
+ return eval(transform(code));
+};
+
+var run = exports.run = function(code) {
+ var jsx = docblock.parseAsObject(docblock.extract(code)).jsx;
+
+ var functionBody = jsx ? transform(code).code : code;
+ var scriptEl = document.createElement('script');
+
+ scriptEl.innerHTML = functionBody;
+ headEl.appendChild(scriptEl);
+};
+
+if (typeof window === "undefined" || window === null) {
+ return;
+}
+
+var load = exports.load = function(url, callback) {
+ var xhr;
+ xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP')
+ : new XMLHttpRequest();
+ // Disable async since we need to execute scripts in the order they are in the
+ // DOM to mirror normal script loading.
+ xhr.open('GET', url, false);
+ if ('overrideMimeType' in xhr) {
+ xhr.overrideMimeType('text/plain');
+ }
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 0 || xhr.status === 200) {
+ run(xhr.responseText);
+ } else {
+ throw new Error("Could not load " + url);
+ }
+ if (callback) {
+ return callback();
+ }
+ }
+ };
+ return xhr.send(null);
+};
+
+runScripts = function() {
+ var scripts = document.getElementsByTagName('script');
+ scripts = Array.prototype.slice.call(scripts);
+ var jsxScripts = scripts.filter(function(script) {
+ return script.type === 'text/jsx';
+ });
+
+ jsxScripts.forEach(function(script) {
+ if (script.src) {
+ load(script.src);
+ } else {
+ run(script.innerHTML);
+ }
+ });
+};
+
+if (window.addEventListener) {
+ window.addEventListener('DOMContentLoaded', runScripts, false);
+} else {
+ window.attachEvent('onload', runScripts);
+}
+
+},{"./fbtransform/lib/docblock":16,"./fbtransform/lib/transform":17,"./fbtransform/visitors":23}],16:[function(require,module,exports){
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var docblockRe = /^\s*(\/\*\*(.|\n)*?\*\/)/;
+var ltrimRe = /^\s*/;
+/**
+ * @param {String} contents
+ * @return {String}
+ */
+function extract(contents) {
+ var match = contents.match(docblockRe);
+ if (match) {
+ return match[0].replace(ltrimRe, '') || '';
+ }
+ return '';
+}
+
+
+var commentStartRe = /^\/\*\*?/;
+var commentEndRe = /\*\/$/;
+var wsRe = /[\t ]+/g;
+var stringStartRe = /(\n|^) *\*/g;
+var multilineRe = /(?:^|\n) *(@[^\n]*?) *\n *([^@\n\s][^@\n]+?) *\n/g;
+var propertyRe = /(?:^|\n) *@(\S+) *([^\n]*)/g;
+
+/**
+ * @param {String} contents
+ * @return {Array}
+ */
+function parse(docblock) {
+ docblock = docblock
+ .replace(commentStartRe, '')
+ .replace(commentEndRe, '')
+ .replace(wsRe, ' ')
+ .replace(stringStartRe, '$1');
+
+ // Normalize multi-line directives
+ var prev = '';
+ while (prev != docblock) {
+ prev = docblock;
+ docblock = docblock.replace(multilineRe, "\n$1 $2\n");
+ }
+ docblock = docblock.trim();
+
+ var result = [];
+ var match;
+ while (match = propertyRe.exec(docblock)) {
+ result.push([match[1], match[2]]);
+ }
+
+ return result;
+}
+
+/**
+ * Same as parse but returns an object of prop: value instead of array of paris
+ * If a property appers more than once the last one will be returned
+ *
+ * @param {String} contents
+ * @return {Object}
+ */
+function parseAsObject(docblock) {
+ var pairs = parse(docblock);
+ var result = {};
+ for (var i = 0; i < pairs.length; i++) {
+ result[pairs[i][0]] = pairs[i][1];
+ }
+ return result;
+}
+
+
+exports.extract = extract;
+exports.parse = parse;
+exports.parseAsObject = parseAsObject;
+
+},{}],17:[function(require,module,exports){
(function(){/**
* Copyright 2013 Facebook, Inc.
*
@@ -6886,287 +8630,469 @@ parseYieldExpression: true
* limitations under the License.
*/
/*global exports:true*/
+/*jslint node: true*/
"use strict";
-var catchup = require('../lib/utils').catchup;
-var append = require('../lib/utils').append;
-var move = require('../lib/utils').move;
-var knownTags = {
- a: true,
- abbr: true,
- address: true,
- applet: true,
- area: true,
- article: true,
- aside: true,
- audio: true,
- b: true,
- base: true,
- bdi: true,
- bdo: true,
- blockquote: true,
- body: true,
- br: true,
- button: true,
- canvas: true,
- circle: true,
- ellipse: true,
- caption: true,
- cite: true,
- code: true,
- col: true,
- colgroup: true,
- command: true,
- data: true,
- datalist: true,
- dd: true,
- del: true,
- details: true,
- dfn: true,
- dialog: true,
- div: true,
- dl: true,
- dt: true,
- em: true,
- embed: true,
- fieldset: true,
- figcaption: true,
- figure: true,
- footer: true,
- form: true,
- g: true,
- h1: true,
- h2: true,
- h3: true,
- h4: true,
- h5: true,
- h6: true,
- head: true,
- header: true,
- hgroup: true,
- hr: true,
- html: true,
- i: true,
- iframe: true,
- img: true,
- input: true,
- ins: true,
- kbd: true,
- keygen: true,
- label: true,
- legend: true,
- li: true,
- line: true,
- link: true,
- map: true,
- mark: true,
- marquee: true,
- menu: true,
- meta: true,
- meter: true,
- nav: true,
- noscript: true,
- object: true,
- ol: true,
- optgroup: true,
- option: true,
- output: true,
- p: true,
- path: true,
- param: true,
- pre: true,
- progress: true,
- q: true,
- rect: true,
- rp: true,
- rt: true,
- ruby: true,
- s: true,
- samp: true,
- script: true,
- section: true,
- select: true,
- small: true,
- source: true,
- span: true,
- strong: true,
- style: true,
- sub: true,
- summary: true,
- sup: true,
- svg: true,
- table: true,
- tbody: true,
- td: true,
- text: true,
- textarea: true,
- tfoot: true,
- th: true,
- thead: true,
- time: true,
- title: true,
- tr: true,
- track: true,
- u: true,
- ul: true,
- 'var': true,
- video: true,
- wbr: true
-};
+/**
+ * Syntax transfomer for javascript. Takes the source in, spits the source
+ * out. Tries to maintain readability and preserve whitespace and line numbers
+ * where posssible.
+ *
+ * Support
+ * - ES6 class transformation + private property munging, see ./classes.js
+ * - React XHP style syntax transformations, see ./react.js
+ * - Bolt XHP style syntax transformations, see ./bolt.js
+ *
+ * The general flow is the following:
+ * - Parse the source with our customized esprima-parser
+ * https://github.com/voloko/esprima. We have to customize the parser to
+ * support non-standard XHP-style syntax. We parse the source range: true
+ * option that forces esprima to return positions in the source within
+ * resulting parse tree.
+ *
+ * - Traverse resulting syntax tree, trying to apply a set of visitors to each
+ * node. Each visitor should provide a .test() function that tests if the
+ * visitor can process a given node.
+ *
+ * - Visitor is responsible for code generation for a given syntax node.
+ * Generated code is stored in state.g.buffer that is passed to every
+ * visitor. It's up to the visitor to process the code the way it sees fit.
+ * All of the current visitors however use both the node and the original
+ * source to generate transformed code. They use nodes to generate new
+ * code and they copy the original source, preserving whitespace and comments,
+ * for the parts they don't care about.
+ */
+var esprima = require('esprima');
-function safeTrim(string) {
- return string.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
+var createState = require('./utils').createState;
+var catchup = require('./utils').catchup;
+
+/**
+ * @param {object} object
+ * @param {function} visitor
+ * @param {array} path
+ * @param {object} state
+ */
+function traverse(object, path, state) {
+ var key, child;
+
+ if (walker(traverse, object, path, state) === false) {
+ return;
+ }
+ path.unshift(object);
+ for (key in object) {
+ // skip obviously wrong attributes
+ if (key === 'range' || key === 'loc') {
+ continue;
+ }
+ if (object.hasOwnProperty(key)) {
+ child = object[key];
+ if (typeof child === 'object' && child !== null) {
+ child.range && catchup(child.range[0], state);
+ traverse(child, path, state);
+ child.range && catchup(child.range[1], state);
+ }
+ }
+ }
+ path.shift();
}
-// Replace all trailing whitespace characters with a single space character
-function trimWithSingleSpace(string) {
- return string.replace(/^[ \t\xA0]{2,}/, ' ').
- replace(/[ \t\xA0]{2,}$/, ' ').replace(/^\s+$/, '');
+function walker(traverse, object, path, state) {
+ var visitors = state.g.visitors;
+ for (var i = 0; i < visitors.length; i++) {
+ if (visitors[i].test(object, path, state)) {
+ return visitors[i](traverse, object, path, state);
+ }
+ }
+}
+
+function runPass(source, visitors, options) {
+ var ast;
+ try {
+ ast = esprima.parse(source, { comment: true, loc: true, range: true });
+ } catch (e) {
+ e.message = 'Parse Error: ' + e.message;
+ throw e;
+ }
+ var state = createState(source, options);
+ state.g.originalProgramAST = ast;
+ state.g.visitors = visitors;
+
+ if (options.sourceMap) {
+ var SourceMapGenerator = require('source-map').SourceMapGenerator;
+ state.g.sourceMap = new SourceMapGenerator({ file: 'transformed.js' });
+ }
+ traverse(ast, [], state);
+ catchup(source.length, state);
+ return state;
}
/**
- * Special handling for multiline string literals
- * print lines:
- *
- * line
- * line
- *
- * as:
- *
- * "line "+
- * "line"
+ * Applies all available transformations to the source
+ * @param {array} visitors
+ * @param {string} source
+ * @param {?object} options
+ * @return {object}
*/
-function renderXJSLiteral(object, isLast, state, start, end) {
- /** Added blank check filtering and triming*/
- var trimmedChildValue = safeTrim(object.value);
+function transform(visitors, source, options) {
+ options = options || {};
- if (trimmedChildValue) {
- // head whitespace
- append(object.value.match(/^[\t ]*/)[0], state);
- if (start) {
- append(start, state);
- }
+ var state = runPass(source, visitors, options);
+ var sourceMap = state.g.sourceMap;
- var trimmedChildValueWithSpace = trimWithSingleSpace(object.value);
-
- /**
- */
- var initialLines = trimmedChildValue.split(/\r\n|\n|\r/);
-
- var lines = initialLines.filter(function(line) {
- return safeTrim(line).length > 0;
- });
-
- var hasInitialNewLine = initialLines[0] !== lines[0];
- var hasFinalNewLine =
- initialLines[initialLines.length - 1] !== lines[lines.length - 1];
-
- var numLines = lines.length;
- lines.forEach(function (line, ii) {
- var lastLine = ii === numLines - 1;
- var trimmedLine = safeTrim(line);
- if (trimmedLine === '' && !lastLine) {
- append(line, state);
- } else {
- var preString = '';
- var postString = '';
- var leading = '';
-
- if (ii === 0) {
- if (hasInitialNewLine) {
- preString = ' ';
- leading = '\n';
- }
- if (trimmedChildValueWithSpace.substring(0, 1) === ' ') {
- // If this is the first line, and the original content starts with
- // whitespace, place a single space at the beginning.
- preString = ' ';
- }
- } else {
- leading = line.match(/^[ \t]*/)[0];
- }
- if (!lastLine || trimmedChildValueWithSpace.substr(
- trimmedChildValueWithSpace.length - 1, 1) === ' ' ||
- hasFinalNewLine
- ) {
- // If either not on the last line, or the original content ends with
- // whitespace, place a single character at the end.
- postString = ' ';
- }
-
- append(
- leading +
- JSON.stringify(
- preString + trimmedLine + postString
- ) +
- (lastLine ? '' : '+') +
- line.match(/[ \t]*$/)[0],
- state);
- }
- if (!lastLine) {
- append('\n', state);
- }
- });
+ if (sourceMap) {
+ return {
+ sourceMap: sourceMap,
+ sourceMapFilename: options.filename || 'source.js',
+ code: state.g.buffer
+ };
} else {
- if (start) {
- append(start, state);
- }
- append('""', state);
+ return {
+ code: state.g.buffer
+ };
}
- if (end) {
- append(end, state);
- }
-
- // add comma before trailing whitespace
- if (!isLast) {
- append(',', state);
- }
-
- // tail whitespace
- append(object.value.match(/[ \t]*$/)[0], state);
- move(object.range[1], state);
}
-function renderXJSExpression(traverse, object, isLast, path, state) {
- // Plus 1 to skip `{`.
- move(object.range[0] + 1, state);
- traverse(object.value, path, state);
- if (!isLast) {
- // If we need to append a comma, make sure to do so after the expression.
- catchup(object.value.range[1], state);
- append(',', state);
- }
- // Minus 1 to skip `}`.
- catchup(object.range[1] - 1, state);
- move(object.range[1], state);
- return false;
-}
-function quoteAttrName(attr) {
- // Quote invalid JS identifiers.
- if (!/^[a-z_$][a-z\d_$]*$/i.test(attr)) {
- return "'" + attr + "'";
- }
- return attr;
-}
-
-exports.knownTags = knownTags;
-exports.renderXJSExpression = renderXJSExpression;
-exports.renderXJSLiteral = renderXJSLiteral;
-exports.quoteAttrName = quoteAttrName;
+exports.transform = transform;
})()
-},{"../lib/utils":8}],10:[function(require,module,exports){
-/*
- * Copyright 2009-2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE.txt or:
- * http://opensource.org/licenses/BSD-3-Clause
+},{"./utils":18,"esprima":4,"source-map":5}],18:[function(require,module,exports){
+(function(){/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-exports.SourceMapGenerator = require('./source-map/source-map-generator').SourceMapGenerator;
-exports.SourceMapConsumer = require('./source-map/source-map-consumer').SourceMapConsumer;
-exports.SourceNode = require('./source-map/source-node').SourceNode;
+/*global exports:true*/
-},{"./source-map/source-map-generator":12,"./source-map/source-map-consumer":13,"./source-map/source-node":14}],5:[function(require,module,exports){
+/**
+ * State represents the given parser state. It has a local and global parts.
+ * Global contains parser position, source, etc. Local contains scope based
+ * properties, like current class name. State should contain all the info
+ * required for transformation. It's the only mandatory object that is being
+ * passed to every function in transform chain.
+ *
+ * @param {String} source
+ * @param {Object} transformOptions
+ * @return {Object}
+ */
+function createState(source, transformOptions) {
+ return {
+ /**
+ * Name of the super class variable
+ * @type {String}
+ */
+ superVar: '',
+ /**
+ * Name of the enclosing class scope
+ * @type {String}
+ */
+ scopeName: '',
+ /**
+ * Global state (not affected by updateState)
+ * @type {Object}
+ */
+ g: {
+ /**
+ * A set of general options that transformations can consider while doing
+ * a transformation:
+ *
+ * - minify
+ * Specifies that transformation steps should do their best to minify
+ * the output source when possible. This is useful for places where
+ * minification optimizations are possible with higher-level context
+ * info than what jsxmin can provide.
+ *
+ * For example, the ES6 class transform will minify munged private
+ * variables if this flag is set.
+ */
+ opts: transformOptions,
+ /**
+ * Current position in the source code
+ * @type {Number}
+ */
+ position: 0,
+ /**
+ * Buffer containing the result
+ * @type {String}
+ */
+ buffer: '',
+ /**
+ * Indentation offset (only negative offset is supported now)
+ * @type {Number}
+ */
+ indentBy: 0,
+ /**
+ * Source that is being transformed
+ * @type {String}
+ */
+ source: source,
+
+ /**
+ * Cached parsed docblock (see getDocblock)
+ * @type {object}
+ */
+ docblock: null,
+
+ /**
+ * Whether the thing was used
+ * @type {Boolean}
+ */
+ tagNamespaceUsed: false,
+
+ /**
+ * If using bolt xjs transformation
+ * @type {Boolean}
+ */
+ isBolt: undefined,
+
+ /**
+ * Whether to record source map (expensive) or not
+ * @type {SourceMapGenerator|null}
+ */
+ sourceMap: null,
+
+ /**
+ * Filename of the file being processed. Will be returned as a source
+ * attribute in the source map
+ */
+ sourceMapFilename: 'source.js',
+
+ /**
+ * Only when source map is used: last line in the source for which
+ * source map was generated
+ * @type {Number}
+ */
+ sourceLine: 1,
+
+ /**
+ * Only when source map is used: last line in the buffer for which
+ * source map was generated
+ * @type {Number}
+ */
+ bufferLine: 1,
+
+ /**
+ * The top-level Program AST for the original file.
+ */
+ originalProgramAST: null,
+
+ sourceColumn: 0,
+ bufferColumn: 0
+ }
+ };
+}
+
+/**
+ * Updates a copy of a given state with "update" and returns an updated state.
+ *
+ * @param {Object} state
+ * @param {Object} update
+ * @return {Object}
+ */
+function updateState(state, update) {
+ return {
+ g: state.g,
+ superVar: update.superVar || state.superVar,
+ scopeName: update.scopeName || state.scopeName
+ };
+}
+
+/**
+ * Given a state fill the resulting buffer from the original source up to
+ * the end
+ * @param {Number} end
+ * @param {Object} state
+ * @param {Function?} contentTransformer Optional callback to transform newly
+ * added content.
+ */
+function catchup(end, state, contentTransformer) {
+ if (end < state.g.position) {
+ // cannot move backwards
+ return;
+ }
+ var source = state.g.source.substring(state.g.position, end);
+ var transformed = updateIndent(source, state);
+ if (state.g.sourceMap && transformed) {
+ // record where we are
+ state.g.sourceMap.addMapping({
+ generated: { line: state.g.bufferLine, column: state.g.bufferColumn },
+ original: { line: state.g.sourceLine, column: state.g.sourceColumn },
+ source: state.g.sourceMapFilename
+ });
+
+ // record line breaks in transformed source
+ var sourceLines = source.split('\n');
+ var transformedLines = transformed.split('\n');
+ // Add line break mappings between last known mapping and the end of the
+ // added piece. So for the code piece
+ // (foo, bar);
+ // > var x = 2;
+ // > var b = 3;
+ // var c =
+ // only add lines marked with ">": 2, 3.
+ for (var i = 1; i < sourceLines.length - 1; i++) {
+ state.g.sourceMap.addMapping({
+ generated: { line: state.g.bufferLine, column: 0 },
+ original: { line: state.g.sourceLine, column: 0 },
+ source: state.g.sourceMapFilename
+ });
+ state.g.sourceLine++;
+ state.g.bufferLine++;
+ }
+ // offset for the last piece
+ if (sourceLines.length > 1) {
+ state.g.sourceLine++;
+ state.g.bufferLine++;
+ state.g.sourceColumn = 0;
+ state.g.bufferColumn = 0;
+ }
+ state.g.sourceColumn += sourceLines[sourceLines.length - 1].length;
+ state.g.bufferColumn +=
+ transformedLines[transformedLines.length - 1].length;
+ }
+ state.g.buffer +=
+ contentTransformer ? contentTransformer(transformed) : transformed;
+ state.g.position = end;
+}
+
+/**
+ * Applies `catchup` but passing in a function that removes any non-whitespace
+ * characters.
+ */
+var re = /(\S)/g;
+function stripNonWhite(value) {
+ return value.replace(re, function() {
+ return '';
+ });
+}
+/**
+ * Catches up as `catchup` but turns each non-white character into a space.
+ */
+function catchupWhiteSpace(end, state) {
+ catchup(end, state, stripNonWhite);
+}
+
+/**
+ * Same as catchup but does not touch the buffer
+ * @param {Number} end
+ * @param {Object} state
+ */
+function move(end, state) {
+ // move the internal cursors
+ if (state.g.sourceMap) {
+ if (end < state.g.position) {
+ state.g.position = 0;
+ state.g.sourceLine = 1;
+ state.g.sourceColumn = 0;
+ }
+
+ var source = state.g.source.substring(state.g.position, end);
+ var sourceLines = source.split('\n');
+ if (sourceLines.length > 1) {
+ state.g.sourceLine += sourceLines.length - 1;
+ state.g.sourceColumn = 0;
+ }
+ state.g.sourceColumn += sourceLines[sourceLines.length - 1].length;
+ }
+ state.g.position = end;
+}
+
+/**
+ * Appends a string of text to the buffer
+ * @param {String} string
+ * @param {Object} state
+ */
+function append(string, state) {
+ if (state.g.sourceMap && string) {
+ state.g.sourceMap.addMapping({
+ generated: { line: state.g.bufferLine, column: state.g.bufferColumn },
+ original: { line: state.g.sourceLine, column: state.g.sourceColumn },
+ source: state.g.sourceMapFilename
+ });
+ var transformedLines = string.split('\n');
+ if (transformedLines.length > 1) {
+ state.g.bufferLine += transformedLines.length - 1;
+ state.g.bufferColumn = 0;
+ }
+ state.g.bufferColumn +=
+ transformedLines[transformedLines.length - 1].length;
+ }
+ state.g.buffer += string;
+}
+
+/**
+ * Update indent using state.indentBy property. Indent is measured in
+ * double spaces. Updates a single line only.
+ *
+ * @param {String} str
+ * @param {Object} state
+ * @return {String}
+ */
+function updateIndent(str, state) {
+ for (var i = 0; i < -state.g.indentBy; i++) {
+ str = str.replace(/(^|\n)( {2}|\t)/g, '$1');
+ }
+ return str;
+}
+
+/**
+ * Calculates indent from the beginning of the line until "start" or the first
+ * character before start.
+ * @example
+ * " foo.bar()"
+ * ^
+ * start
+ * indent will be 2
+ *
+ * @param {Number} start
+ * @param {Object} state
+ * @return {Number}
+ */
+function indentBefore(start, state) {
+ var end = start;
+ start = start - 1;
+
+ while (start > 0 && state.g.source[start] != '\n') {
+ if (!state.g.source[start].match(/[ \t]/)) {
+ end = start;
+ }
+ start--;
+ }
+ return state.g.source.substring(start + 1, end);
+}
+
+function getDocblock(state) {
+ if (!state.g.docblock) {
+ var docblock = require('./docblock');
+ state.g.docblock =
+ docblock.parseAsObject(docblock.extract(state.g.source));
+ }
+ return state.g.docblock;
+}
+
+exports.catchup = catchup;
+exports.catchupWhiteSpace = catchupWhiteSpace;
+exports.append = append;
+exports.move = move;
+exports.updateIndent = updateIndent;
+exports.indentBefore = indentBefore;
+exports.updateState = updateState;
+exports.createState = createState;
+exports.getDocblock = getDocblock;
+
+})()
+},{"./docblock":16}],19:[function(require,module,exports){
(function(){/**
* Copyright 2013 Facebook, Inc.
*
@@ -7661,7 +9587,7 @@ exports.visitSuperCall = visitSuperCall;
exports.visitPrivateProperty = visitPrivateProperty;
})()
-},{"../lib/utils":8,"../lib/docblock":4,"esprima":9,"base62":15}],6:[function(require,module,exports){
+},{"../lib/docblock":16,"../lib/utils":18,"base62":1,"esprima":4}],20:[function(require,module,exports){
(function(){/**
* Copyright 2013 Facebook, Inc.
*
@@ -7688,7 +9614,8 @@ var move = require('../lib/utils').move;
var getDocblock = require('../lib/utils').getDocblock;
var FALLBACK_TAGS = require('./xjs').knownTags;
-var renderXJSExpression = require('./xjs').renderXJSExpression;
+var renderXJSExpressionContainer =
+ require('./xjs').renderXJSExpressionContainer;
var renderXJSLiteral = require('./xjs').renderXJSLiteral;
var quoteAttrName = require('./xjs').quoteAttrName;
@@ -7785,7 +9712,7 @@ function visitReactTag(traverse, object, path, state) {
renderXJSLiteral(attr.value, isLast, state);
} else {
move(attr.value.range[0], state);
- renderXJSExpression(traverse, attr.value, isLast, path, state);
+ renderXJSExpressionContainer(traverse, attr.value, isLast, path, state);
}
if (isLast) {
@@ -7800,14 +9727,10 @@ function visitReactTag(traverse, object, path, state) {
move(object.openingElement.range[1], state);
}
- // separate props and children arguments
- append(', ', state);
-
// filter out whitespace
if (childrenToRender.length > 0) {
- if (childrenToRender.length > 1) {
- append('[', state);
- }
+ append(', ', state);
+
object.children.forEach(function(child) {
if (child.type === Syntax.Literal && !child.value.match(/\S/)) {
return;
@@ -7818,8 +9741,8 @@ function visitReactTag(traverse, object, path, state) {
if (child.type === Syntax.Literal) {
renderXJSLiteral(child, isLast, state);
- } else if (child.type === Syntax.XJSExpression) {
- renderXJSExpression(traverse, child, isLast, path, state);
+ } else if (child.type === Syntax.XJSExpressionContainer) {
+ renderXJSExpressionContainer(traverse, child, isLast, path, state);
} else {
traverse(child, path, state);
if (!isLast) {
@@ -7830,8 +9753,6 @@ function visitReactTag(traverse, object, path, state) {
catchup(child.range[1], state);
});
- } else {
- append('null', state);
}
if (object.selfClosing) {
@@ -7844,11 +9765,6 @@ function visitReactTag(traverse, object, path, state) {
move(object.closingElement.range[1], state);
}
- if (childrenToRender.length > 0) {
- if (childrenToRender.length > 1) {
- append(']', state);
- }
- }
append(')', state);
return false;
}
@@ -7862,7 +9778,7 @@ visitReactTag.test = function(object, path, state) {
exports.visitReactTag = visitReactTag;
})()
-},{"../lib/utils":8,"./xjs":11,"esprima":9}],7:[function(require,module,exports){
+},{"../lib/utils":18,"./xjs":22,"esprima":4}],21:[function(require,module,exports){
(function(){/**
* Copyright 2013 Facebook, Inc.
*
@@ -7928,2228 +9844,353 @@ visitReactDisplayName.test = function(object, path, state) {
exports.visitReactDisplayName = visitReactDisplayName;
})()
-},{"../lib/utils":8,"esprima":9}],15:[function(require,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
-},{}],12:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
+},{"../lib/utils":18,"esprima":4}],22:[function(require,module,exports){
+(function(){/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- var base64VLQ = require('./base64-vlq');
- var util = require('./util');
- var ArraySet = require('./array-set').ArraySet;
-
- /**
- * An instance of the SourceMapGenerator represents a source map which is
- * being built incrementally. To create a new one, you must pass an object
- * with the following properties:
- *
- * - file: The filename of the generated source.
- * - sourceRoot: An optional root for all URLs in this source map.
- */
- function SourceMapGenerator(aArgs) {
- this._file = util.getArg(aArgs, 'file');
- this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
- this._sources = new ArraySet();
- this._names = new ArraySet();
- this._mappings = [];
- this._sourcesContents = null;
- }
-
- SourceMapGenerator.prototype._version = 3;
-
- /**
- * Creates a new SourceMapGenerator based on a SourceMapConsumer
- *
- * @param aSourceMapConsumer The SourceMap.
- */
- SourceMapGenerator.fromSourceMap =
- function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
- var sourceRoot = aSourceMapConsumer.sourceRoot;
- var generator = new SourceMapGenerator({
- file: aSourceMapConsumer.file,
- sourceRoot: sourceRoot
- });
- aSourceMapConsumer.eachMapping(function (mapping) {
- var newMapping = {
- generated: {
- line: mapping.generatedLine,
- column: mapping.generatedColumn
- }
- };
-
- if (mapping.source) {
- newMapping.source = mapping.source;
- if (sourceRoot) {
- newMapping.source = util.relative(sourceRoot, newMapping.source);
- }
-
- newMapping.original = {
- line: mapping.originalLine,
- column: mapping.originalColumn
- };
-
- if (mapping.name) {
- newMapping.name = mapping.name;
- }
- }
-
- generator.addMapping(newMapping);
- });
- aSourceMapConsumer.sources.forEach(function (sourceFile) {
- var content = aSourceMapConsumer.sourceContentFor(sourceFile);
- if (content) {
- generator.setSourceContent(sourceFile, content);
- }
- });
- return generator;
- };
-
- /**
- * Add a single mapping from original source line and column to the generated
- * source's line and column for this source map being created. The mapping
- * object should have the following properties:
- *
- * - generated: An object with the generated line and column positions.
- * - original: An object with the original line and column positions.
- * - source: The original source file (relative to the sourceRoot).
- * - name: An optional original token name for this mapping.
- */
- SourceMapGenerator.prototype.addMapping =
- function SourceMapGenerator_addMapping(aArgs) {
- var generated = util.getArg(aArgs, 'generated');
- var original = util.getArg(aArgs, 'original', null);
- var source = util.getArg(aArgs, 'source', null);
- var name = util.getArg(aArgs, 'name', null);
-
- this._validateMapping(generated, original, source, name);
-
- if (source && !this._sources.has(source)) {
- this._sources.add(source);
- }
-
- if (name && !this._names.has(name)) {
- this._names.add(name);
- }
-
- this._mappings.push({
- generated: generated,
- original: original,
- source: source,
- name: name
- });
- };
-
- /**
- * Set the source content for a source file.
- */
- SourceMapGenerator.prototype.setSourceContent =
- function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
- var source = aSourceFile;
- if (this._sourceRoot) {
- source = util.relative(this._sourceRoot, source);
- }
-
- if (aSourceContent !== null) {
- // Add the source content to the _sourcesContents map.
- // Create a new _sourcesContents map if the property is null.
- if (!this._sourcesContents) {
- this._sourcesContents = {};
- }
- this._sourcesContents[util.toSetString(source)] = aSourceContent;
- } else {
- // Remove the source file from the _sourcesContents map.
- // If the _sourcesContents map is empty, set the property to null.
- delete this._sourcesContents[util.toSetString(source)];
- if (Object.keys(this._sourcesContents).length === 0) {
- this._sourcesContents = null;
- }
- }
- };
-
- /**
- * Applies the mappings of a sub-source-map for a specific source file to the
- * source map being generated. Each mapping to the supplied source file is
- * rewritten using the supplied source map. Note: The resolution for the
- * resulting mappings is the minimium of this map and the supplied map.
- *
- * @param aSourceMapConsumer The source map to be applied.
- * @param aSourceFile Optional. The filename of the source file.
- * If omitted, SourceMapConsumer's file property will be used.
- */
- SourceMapGenerator.prototype.applySourceMap =
- function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
- // If aSourceFile is omitted, we will use the file property of the SourceMap
- if (!aSourceFile) {
- aSourceFile = aSourceMapConsumer.file;
- }
- var sourceRoot = this._sourceRoot;
- // Make "aSourceFile" relative if an absolute Url is passed.
- if (sourceRoot) {
- aSourceFile = util.relative(sourceRoot, aSourceFile);
- }
- // Applying the SourceMap can add and remove items from the sources and
- // the names array.
- var newSources = new ArraySet();
- var newNames = new ArraySet();
-
- // Find mappings for the "aSourceFile"
- this._mappings.forEach(function (mapping) {
- if (mapping.source === aSourceFile && mapping.original) {
- // Check if it can be mapped by the source map, then update the mapping.
- var original = aSourceMapConsumer.originalPositionFor({
- line: mapping.original.line,
- column: mapping.original.column
- });
- if (original.source !== null) {
- // Copy mapping
- if (sourceRoot) {
- mapping.source = util.relative(sourceRoot, original.source);
- } else {
- mapping.source = original.source;
- }
- mapping.original.line = original.line;
- mapping.original.column = original.column;
- if (original.name !== null && mapping.name !== null) {
- // Only use the identifier name if it's an identifier
- // in both SourceMaps
- mapping.name = original.name;
- }
- }
- }
-
- var source = mapping.source;
- if (source && !newSources.has(source)) {
- newSources.add(source);
- }
-
- var name = mapping.name;
- if (name && !newNames.has(name)) {
- newNames.add(name);
- }
-
- }, this);
- this._sources = newSources;
- this._names = newNames;
-
- // Copy sourcesContents of applied map.
- aSourceMapConsumer.sources.forEach(function (sourceFile) {
- var content = aSourceMapConsumer.sourceContentFor(sourceFile);
- if (content) {
- if (sourceRoot) {
- sourceFile = util.relative(sourceRoot, sourceFile);
- }
- this.setSourceContent(sourceFile, content);
- }
- }, this);
- };
-
- /**
- * A mapping can have one of the three levels of data:
- *
- * 1. Just the generated position.
- * 2. The Generated position, original position, and original source.
- * 3. Generated and original position, original source, as well as a name
- * token.
- *
- * To maintain consistency, we validate that any new mapping being added falls
- * in to one of these categories.
- */
- SourceMapGenerator.prototype._validateMapping =
- function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
- aName) {
- if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
- && aGenerated.line > 0 && aGenerated.column >= 0
- && !aOriginal && !aSource && !aName) {
- // Case 1.
- return;
- }
- else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
- && aOriginal && 'line' in aOriginal && 'column' in aOriginal
- && aGenerated.line > 0 && aGenerated.column >= 0
- && aOriginal.line > 0 && aOriginal.column >= 0
- && aSource) {
- // Cases 2 and 3.
- return;
- }
- else {
- throw new Error('Invalid mapping.');
- }
- };
-
- function cmpLocation(loc1, loc2) {
- var cmp = (loc1 && loc1.line) - (loc2 && loc2.line);
- return cmp ? cmp : (loc1 && loc1.column) - (loc2 && loc2.column);
- }
-
- function strcmp(str1, str2) {
- str1 = str1 || '';
- str2 = str2 || '';
- return (str1 > str2) - (str1 < str2);
- }
-
- function cmpMapping(mappingA, mappingB) {
- return cmpLocation(mappingA.generated, mappingB.generated) ||
- cmpLocation(mappingA.original, mappingB.original) ||
- strcmp(mappingA.source, mappingB.source) ||
- strcmp(mappingA.name, mappingB.name);
- }
-
- /**
- * Serialize the accumulated mappings in to the stream of base 64 VLQs
- * specified by the source map format.
- */
- SourceMapGenerator.prototype._serializeMappings =
- function SourceMapGenerator_serializeMappings() {
- var previousGeneratedColumn = 0;
- var previousGeneratedLine = 1;
- var previousOriginalColumn = 0;
- var previousOriginalLine = 0;
- var previousName = 0;
- var previousSource = 0;
- var result = '';
- var mapping;
-
- // The mappings must be guarenteed to be in sorted order before we start
- // serializing them or else the generated line numbers (which are defined
- // via the ';' separators) will be all messed up. Note: it might be more
- // performant to maintain the sorting as we insert them, rather than as we
- // serialize them, but the big O is the same either way.
- this._mappings.sort(cmpMapping);
-
- for (var i = 0, len = this._mappings.length; i < len; i++) {
- mapping = this._mappings[i];
-
- if (mapping.generated.line !== previousGeneratedLine) {
- previousGeneratedColumn = 0;
- while (mapping.generated.line !== previousGeneratedLine) {
- result += ';';
- previousGeneratedLine++;
- }
- }
- else {
- if (i > 0) {
- if (!cmpMapping(mapping, this._mappings[i - 1])) {
- continue;
- }
- result += ',';
- }
- }
-
- result += base64VLQ.encode(mapping.generated.column
- - previousGeneratedColumn);
- previousGeneratedColumn = mapping.generated.column;
-
- if (mapping.source && mapping.original) {
- result += base64VLQ.encode(this._sources.indexOf(mapping.source)
- - previousSource);
- previousSource = this._sources.indexOf(mapping.source);
-
- // lines are stored 0-based in SourceMap spec version 3
- result += base64VLQ.encode(mapping.original.line - 1
- - previousOriginalLine);
- previousOriginalLine = mapping.original.line - 1;
-
- result += base64VLQ.encode(mapping.original.column
- - previousOriginalColumn);
- previousOriginalColumn = mapping.original.column;
-
- if (mapping.name) {
- result += base64VLQ.encode(this._names.indexOf(mapping.name)
- - previousName);
- previousName = this._names.indexOf(mapping.name);
- }
- }
- }
-
- return result;
- };
-
- /**
- * Externalize the source map.
- */
- SourceMapGenerator.prototype.toJSON =
- function SourceMapGenerator_toJSON() {
- var map = {
- version: this._version,
- file: this._file,
- sources: this._sources.toArray(),
- names: this._names.toArray(),
- mappings: this._serializeMappings()
- };
- if (this._sourceRoot) {
- map.sourceRoot = this._sourceRoot;
- }
- if (this._sourcesContents) {
- map.sourcesContent = map.sources.map(function (source) {
- if (map.sourceRoot) {
- source = util.relative(map.sourceRoot, source);
- }
- return Object.prototype.hasOwnProperty.call(
- this._sourcesContents, util.toSetString(source))
- ? this._sourcesContents[util.toSetString(source)]
- : null;
- }, this);
- }
- return map;
- };
-
- /**
- * Render the source map being generated to a string.
- */
- SourceMapGenerator.prototype.toString =
- function SourceMapGenerator_toString() {
- return JSON.stringify(this);
- };
-
- exports.SourceMapGenerator = SourceMapGenerator;
-
-});
-
-},{"./base64-vlq":16,"./util":17,"./array-set":18,"amdefine":19}],13:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- var util = require('./util');
- var binarySearch = require('./binary-search');
- var ArraySet = require('./array-set').ArraySet;
- var base64VLQ = require('./base64-vlq');
-
- /**
- * A SourceMapConsumer instance represents a parsed source map which we can
- * query for information about the original file positions by giving it a file
- * position in the generated source.
- *
- * The only parameter is the raw source map (either as a JSON string, or
- * already parsed to an object). According to the spec, source maps have the
- * following attributes:
- *
- * - version: Which version of the source map spec this map is following.
- * - sources: An array of URLs to the original source files.
- * - names: An array of identifiers which can be referrenced by individual mappings.
- * - sourceRoot: Optional. The URL root from which all sources are relative.
- * - sourcesContent: Optional. An array of contents of the original source files.
- * - mappings: A string of base64 VLQs which contain the actual mappings.
- * - file: The generated file this source map is associated with.
- *
- * Here is an example source map, taken from the source map spec[0]:
- *
- * {
- * version : 3,
- * file: "out.js",
- * sourceRoot : "",
- * sources: ["foo.js", "bar.js"],
- * names: ["src", "maps", "are", "fun"],
- * mappings: "AA,AB;;ABCDE;"
- * }
- *
- * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
- */
- function SourceMapConsumer(aSourceMap) {
- var sourceMap = aSourceMap;
- if (typeof aSourceMap === 'string') {
- sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
- }
-
- var version = util.getArg(sourceMap, 'version');
- var sources = util.getArg(sourceMap, 'sources');
- var names = util.getArg(sourceMap, 'names');
- var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
- var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
- var mappings = util.getArg(sourceMap, 'mappings');
- var file = util.getArg(sourceMap, 'file');
-
- if (version !== this._version) {
- throw new Error('Unsupported version: ' + version);
- }
-
- this._names = ArraySet.fromArray(names);
- this._sources = ArraySet.fromArray(sources);
- this.sourceRoot = sourceRoot;
- this.sourcesContent = sourcesContent;
- this.file = file;
-
- // `this._generatedMappings` and `this._originalMappings` hold the parsed
- // mapping coordinates from the source map's "mappings" attribute. Each
- // object in the array is of the form
- //
- // {
- // generatedLine: The line number in the generated code,
- // generatedColumn: The column number in the generated code,
- // source: The path to the original source file that generated this
- // chunk of code,
- // originalLine: The line number in the original source that
- // corresponds to this chunk of generated code,
- // originalColumn: The column number in the original source that
- // corresponds to this chunk of generated code,
- // name: The name of the original symbol which generated this chunk of
- // code.
- // }
- //
- // All properties except for `generatedLine` and `generatedColumn` can be
- // `null`.
- //
- // `this._generatedMappings` is ordered by the generated positions.
- //
- // `this._originalMappings` is ordered by the original positions.
- this._generatedMappings = [];
- this._originalMappings = [];
- this._parseMappings(mappings, sourceRoot);
- }
-
- /**
- * The version of the source mapping spec that we are consuming.
- */
- SourceMapConsumer.prototype._version = 3;
-
- /**
- * The list of original sources.
- */
- Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
- get: function () {
- return this._sources.toArray().map(function (s) {
- return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
- }, this);
- }
- });
-
- /**
- * Parse the mappings in a string in to a data structure which we can easily
- * query (an ordered list in this._generatedMappings).
- */
- SourceMapConsumer.prototype._parseMappings =
- function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
- var generatedLine = 1;
- var previousGeneratedColumn = 0;
- var previousOriginalLine = 0;
- var previousOriginalColumn = 0;
- var previousSource = 0;
- var previousName = 0;
- var mappingSeparator = /^[,;]/;
- var str = aStr;
- var mapping;
- var temp;
-
- while (str.length > 0) {
- if (str.charAt(0) === ';') {
- generatedLine++;
- str = str.slice(1);
- previousGeneratedColumn = 0;
- }
- else if (str.charAt(0) === ',') {
- str = str.slice(1);
- }
- else {
- mapping = {};
- mapping.generatedLine = generatedLine;
-
- // Generated column.
- temp = base64VLQ.decode(str);
- mapping.generatedColumn = previousGeneratedColumn + temp.value;
- previousGeneratedColumn = mapping.generatedColumn;
- str = temp.rest;
-
- if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
- // Original source.
- temp = base64VLQ.decode(str);
- mapping.source = this._sources.at(previousSource + temp.value);
- previousSource += temp.value;
- str = temp.rest;
- if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
- throw new Error('Found a source, but no line and column');
- }
-
- // Original line.
- temp = base64VLQ.decode(str);
- mapping.originalLine = previousOriginalLine + temp.value;
- previousOriginalLine = mapping.originalLine;
- // Lines are stored 0-based
- mapping.originalLine += 1;
- str = temp.rest;
- if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
- throw new Error('Found a source and line, but no column');
- }
-
- // Original column.
- temp = base64VLQ.decode(str);
- mapping.originalColumn = previousOriginalColumn + temp.value;
- previousOriginalColumn = mapping.originalColumn;
- str = temp.rest;
-
- if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
- // Original name.
- temp = base64VLQ.decode(str);
- mapping.name = this._names.at(previousName + temp.value);
- previousName += temp.value;
- str = temp.rest;
- }
- }
-
- this._generatedMappings.push(mapping);
- if (typeof mapping.originalLine === 'number') {
- this._originalMappings.push(mapping);
- }
- }
- }
-
- this._originalMappings.sort(this._compareOriginalPositions);
- };
-
- /**
- * Comparator between two mappings where the original positions are compared.
- */
- SourceMapConsumer.prototype._compareOriginalPositions =
- function SourceMapConsumer_compareOriginalPositions(mappingA, mappingB) {
- if (mappingA.source > mappingB.source) {
- return 1;
- }
- else if (mappingA.source < mappingB.source) {
- return -1;
- }
- else {
- var cmp = mappingA.originalLine - mappingB.originalLine;
- return cmp === 0
- ? mappingA.originalColumn - mappingB.originalColumn
- : cmp;
- }
- };
-
- /**
- * Comparator between two mappings where the generated positions are compared.
- */
- SourceMapConsumer.prototype._compareGeneratedPositions =
- function SourceMapConsumer_compareGeneratedPositions(mappingA, mappingB) {
- var cmp = mappingA.generatedLine - mappingB.generatedLine;
- return cmp === 0
- ? mappingA.generatedColumn - mappingB.generatedColumn
- : cmp;
- };
-
- /**
- * Find the mapping that best matches the hypothetical "needle" mapping that
- * we are searching for in the given "haystack" of mappings.
- */
- SourceMapConsumer.prototype._findMapping =
- function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
- aColumnName, aComparator) {
- // To return the position we are searching for, we must first find the
- // mapping for the given position and then return the opposite position it
- // points to. Because the mappings are sorted, we can use binary search to
- // find the best mapping.
-
- if (aNeedle[aLineName] <= 0) {
- throw new TypeError('Line must be greater than or equal to 1, got '
- + aNeedle[aLineName]);
- }
- if (aNeedle[aColumnName] < 0) {
- throw new TypeError('Column must be greater than or equal to 0, got '
- + aNeedle[aColumnName]);
- }
-
- return binarySearch.search(aNeedle, aMappings, aComparator);
- };
-
- /**
- * Returns the original source, line, and column information for the generated
- * source's line and column positions provided. The only argument is an object
- * with the following properties:
- *
- * - line: The line number in the generated source.
- * - column: The column number in the generated source.
- *
- * and an object is returned with the following properties:
- *
- * - source: The original source file, or null.
- * - line: The line number in the original source, or null.
- * - column: The column number in the original source, or null.
- * - name: The original identifier, or null.
- */
- SourceMapConsumer.prototype.originalPositionFor =
- function SourceMapConsumer_originalPositionFor(aArgs) {
- var needle = {
- generatedLine: util.getArg(aArgs, 'line'),
- generatedColumn: util.getArg(aArgs, 'column')
- };
-
- var mapping = this._findMapping(needle,
- this._generatedMappings,
- "generatedLine",
- "generatedColumn",
- this._compareGeneratedPositions);
-
- if (mapping) {
- var source = util.getArg(mapping, 'source', null);
- if (source && this.sourceRoot) {
- source = util.join(this.sourceRoot, source);
- }
- return {
- source: source,
- line: util.getArg(mapping, 'originalLine', null),
- column: util.getArg(mapping, 'originalColumn', null),
- name: util.getArg(mapping, 'name', null)
- };
- }
-
- return {
- source: null,
- line: null,
- column: null,
- name: null
- };
- };
-
- /**
- * Returns the original source content. The only argument is
- * the url of the original source file. Returns null if no
- * original source content is availible.
- */
- SourceMapConsumer.prototype.sourceContentFor =
- function SourceMapConsumer_sourceContentFor(aSource) {
- if (!this.sourcesContent) {
- return null;
- }
-
- if (this.sourceRoot) {
- aSource = util.relative(this.sourceRoot, aSource);
- }
-
- if (this._sources.has(aSource)) {
- return this.sourcesContent[this._sources.indexOf(aSource)];
- }
-
- var url;
- if (this.sourceRoot
- && (url = util.urlParse(this.sourceRoot))
- && (!url.path || url.path == "/")
- && this._sources.has("/" + aSource)) {
- return this.sourcesContent[this._sources.indexOf("/" + aSource)];
- }
-
- throw new Error('"' + aSource + '" is not in the SourceMap.');
- };
-
- /**
- * Returns the generated line and column information for the original source,
- * line, and column positions provided. The only argument is an object with
- * the following properties:
- *
- * - source: The filename of the original source.
- * - line: The line number in the original source.
- * - column: The column number in the original source.
- *
- * and an object is returned with the following properties:
- *
- * - line: The line number in the generated source, or null.
- * - column: The column number in the generated source, or null.
- */
- SourceMapConsumer.prototype.generatedPositionFor =
- function SourceMapConsumer_generatedPositionFor(aArgs) {
- var needle = {
- source: util.getArg(aArgs, 'source'),
- originalLine: util.getArg(aArgs, 'line'),
- originalColumn: util.getArg(aArgs, 'column')
- };
-
- if (this.sourceRoot) {
- needle.source = util.relative(this.sourceRoot, needle.source);
- }
-
- var mapping = this._findMapping(needle,
- this._originalMappings,
- "originalLine",
- "originalColumn",
- this._compareOriginalPositions);
-
- if (mapping) {
- return {
- line: util.getArg(mapping, 'generatedLine', null),
- column: util.getArg(mapping, 'generatedColumn', null)
- };
- }
-
- return {
- line: null,
- column: null
- };
- };
-
- SourceMapConsumer.GENERATED_ORDER = 1;
- SourceMapConsumer.ORIGINAL_ORDER = 2;
-
- /**
- * Iterate over each mapping between an original source/line/column and a
- * generated line/column in this source map.
- *
- * @param Function aCallback
- * The function that is called with each mapping.
- * @param Object aContext
- * Optional. If specified, this object will be the value of `this` every
- * time that `aCallback` is called.
- * @param aOrder
- * Either `SourceMapConsumer.GENERATED_ORDER` or
- * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
- * iterate over the mappings sorted by the generated file's line/column
- * order or the original's source/line/column order, respectively. Defaults to
- * `SourceMapConsumer.GENERATED_ORDER`.
- */
- SourceMapConsumer.prototype.eachMapping =
- function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
- var context = aContext || null;
- var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
-
- var mappings;
- switch (order) {
- case SourceMapConsumer.GENERATED_ORDER:
- mappings = this._generatedMappings;
- break;
- case SourceMapConsumer.ORIGINAL_ORDER:
- mappings = this._originalMappings;
- break;
- default:
- throw new Error("Unknown order of iteration.");
- }
-
- var sourceRoot = this.sourceRoot;
- mappings.map(function (mapping) {
- var source = mapping.source;
- if (source && sourceRoot) {
- source = util.join(sourceRoot, source);
- }
- return {
- source: source,
- generatedLine: mapping.generatedLine,
- generatedColumn: mapping.generatedColumn,
- originalLine: mapping.originalLine,
- originalColumn: mapping.originalColumn,
- name: mapping.name
- };
- }).forEach(aCallback, context);
- };
-
- exports.SourceMapConsumer = SourceMapConsumer;
-
-});
-
-},{"./util":17,"./binary-search":20,"./array-set":18,"./base64-vlq":16,"amdefine":19}],14:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
- var util = require('./util');
-
- /**
- * SourceNodes provide a way to abstract over interpolating/concatenating
- * snippets of generated JavaScript source code while maintaining the line and
- * column information associated with the original source code.
- *
- * @param aLine The original line number.
- * @param aColumn The original column number.
- * @param aSource The original source's filename.
- * @param aChunks Optional. An array of strings which are snippets of
- * generated JS, or other SourceNodes.
- * @param aName The original identifier.
- */
- function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
- this.children = [];
- this.sourceContents = {};
- this.line = aLine === undefined ? null : aLine;
- this.column = aColumn === undefined ? null : aColumn;
- this.source = aSource === undefined ? null : aSource;
- this.name = aName === undefined ? null : aName;
- if (aChunks != null) this.add(aChunks);
- }
-
- /**
- * Creates a SourceNode from generated code and a SourceMapConsumer.
- *
- * @param aGeneratedCode The generated code
- * @param aSourceMapConsumer The SourceMap for the generated code
- */
- SourceNode.fromStringWithSourceMap =
- function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
- // The SourceNode we want to fill with the generated code
- // and the SourceMap
- var node = new SourceNode();
-
- // The generated code
- // Processed fragments are removed from this array.
- var remainingLines = aGeneratedCode.split('\n');
-
- // We need to remember the position of "remainingLines"
- var lastGeneratedLine = 1, lastGeneratedColumn = 0;
-
- // The generate SourceNodes we need a code range.
- // To extract it current and last mapping is used.
- // Here we store the last mapping.
- var lastMapping = null;
-
- aSourceMapConsumer.eachMapping(function (mapping) {
- if (lastMapping === null) {
- // We add the generated code until the first mapping
- // to the SourceNode without any mapping.
- // Each line is added as separate string.
- while (lastGeneratedLine < mapping.generatedLine) {
- node.add(remainingLines.shift() + "\n");
- lastGeneratedLine++;
- }
- if (lastGeneratedColumn < mapping.generatedColumn) {
- var nextLine = remainingLines[0];
- node.add(nextLine.substr(0, mapping.generatedColumn));
- remainingLines[0] = nextLine.substr(mapping.generatedColumn);
- lastGeneratedColumn = mapping.generatedColumn;
- }
- } else {
- // We add the code from "lastMapping" to "mapping":
- // First check if there is a new line in between.
- if (lastGeneratedLine < mapping.generatedLine) {
- var code = "";
- // Associate full lines with "lastMapping"
- do {
- code += remainingLines.shift() + "\n";
- lastGeneratedLine++;
- lastGeneratedColumn = 0;
- } while (lastGeneratedLine < mapping.generatedLine);
- // When we reached the correct line, we add code until we
- // reach the correct column too.
- if (lastGeneratedColumn < mapping.generatedColumn) {
- var nextLine = remainingLines[0];
- code += nextLine.substr(0, mapping.generatedColumn);
- remainingLines[0] = nextLine.substr(mapping.generatedColumn);
- lastGeneratedColumn = mapping.generatedColumn;
- }
- // Create the SourceNode.
- addMappingWithCode(lastMapping, code);
- } else {
- // There is no new line in between.
- // Associate the code between "lastGeneratedColumn" and
- // "mapping.generatedColumn" with "lastMapping"
- var nextLine = remainingLines[0];
- var code = nextLine.substr(0, mapping.generatedColumn -
- lastGeneratedColumn);
- remainingLines[0] = nextLine.substr(mapping.generatedColumn -
- lastGeneratedColumn);
- lastGeneratedColumn = mapping.generatedColumn;
- addMappingWithCode(lastMapping, code);
- }
- }
- lastMapping = mapping;
- }, this);
- // We have processed all mappings.
- // Associate the remaining code in the current line with "lastMapping"
- // and add the remaining lines without any mapping
- addMappingWithCode(lastMapping, remainingLines.join("\n"));
-
- // Copy sourcesContent into SourceNode
- aSourceMapConsumer.sources.forEach(function (sourceFile) {
- var content = aSourceMapConsumer.sourceContentFor(sourceFile);
- if (content) {
- node.setSourceContent(sourceFile, content);
- }
- });
-
- return node;
-
- function addMappingWithCode(mapping, code) {
- if (mapping.source === undefined) {
- node.add(code);
- } else {
- node.add(new SourceNode(mapping.originalLine,
- mapping.originalColumn,
- mapping.source,
- code,
- mapping.name));
- }
- }
- };
-
- /**
- * Add a chunk of generated JS to this source node.
- *
- * @param aChunk A string snippet of generated JS code, another instance of
- * SourceNode, or an array where each member is one of those things.
- */
- SourceNode.prototype.add = function SourceNode_add(aChunk) {
- if (Array.isArray(aChunk)) {
- aChunk.forEach(function (chunk) {
- this.add(chunk);
- }, this);
- }
- else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
- if (aChunk) {
- this.children.push(aChunk);
- }
- }
- else {
- throw new TypeError(
- "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
- );
- }
- return this;
- };
-
- /**
- * Add a chunk of generated JS to the beginning of this source node.
- *
- * @param aChunk A string snippet of generated JS code, another instance of
- * SourceNode, or an array where each member is one of those things.
- */
- SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
- if (Array.isArray(aChunk)) {
- for (var i = aChunk.length-1; i >= 0; i--) {
- this.prepend(aChunk[i]);
- }
- }
- else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
- this.children.unshift(aChunk);
- }
- else {
- throw new TypeError(
- "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
- );
- }
- return this;
- };
-
- /**
- * Walk over the tree of JS snippets in this node and its children. The
- * walking function is called once for each snippet of JS and is passed that
- * snippet and the its original associated source's line/column location.
- *
- * @param aFn The traversal function.
- */
- SourceNode.prototype.walk = function SourceNode_walk(aFn) {
- this.children.forEach(function (chunk) {
- if (chunk instanceof SourceNode) {
- chunk.walk(aFn);
- }
- else {
- if (chunk !== '') {
- aFn(chunk, { source: this.source,
- line: this.line,
- column: this.column,
- name: this.name });
- }
- }
- }, this);
- };
-
- /**
- * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
- * each of `this.children`.
- *
- * @param aSep The separator.
- */
- SourceNode.prototype.join = function SourceNode_join(aSep) {
- var newChildren;
- var i;
- var len = this.children.length;
- if (len > 0) {
- newChildren = [];
- for (i = 0; i < len-1; i++) {
- newChildren.push(this.children[i]);
- newChildren.push(aSep);
- }
- newChildren.push(this.children[i]);
- this.children = newChildren;
- }
- return this;
- };
-
- /**
- * Call String.prototype.replace on the very right-most source snippet. Useful
- * for trimming whitespace from the end of a source node, etc.
- *
- * @param aPattern The pattern to replace.
- * @param aReplacement The thing to replace the pattern with.
- */
- SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
- var lastChild = this.children[this.children.length - 1];
- if (lastChild instanceof SourceNode) {
- lastChild.replaceRight(aPattern, aReplacement);
- }
- else if (typeof lastChild === 'string') {
- this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
- }
- else {
- this.children.push(''.replace(aPattern, aReplacement));
- }
- return this;
- };
-
- /**
- * Set the source content for a source file. This will be added to the SourceMapGenerator
- * in the sourcesContent field.
- *
- * @param aSourceFile The filename of the source file
- * @param aSourceContent The content of the source file
- */
- SourceNode.prototype.setSourceContent =
- function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
- this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
- };
-
- /**
- * Walk over the tree of SourceNodes. The walking function is called for each
- * source file content and is passed the filename and source content.
- *
- * @param aFn The traversal function.
- */
- SourceNode.prototype.walkSourceContents =
- function SourceNode_walkSourceContents(aFn) {
- this.children.forEach(function (chunk) {
- if (chunk instanceof SourceNode) {
- chunk.walkSourceContents(aFn);
- }
- }, this);
- Object.keys(this.sourceContents).forEach(function (sourceFileKey) {
- aFn(util.fromSetString(sourceFileKey), this.sourceContents[sourceFileKey]);
- }, this);
- };
-
- /**
- * Return the string representation of this source node. Walks over the tree
- * and concatenates all the various snippets together to one string.
- */
- SourceNode.prototype.toString = function SourceNode_toString() {
- var str = "";
- this.walk(function (chunk) {
- str += chunk;
- });
- return str;
- };
-
- /**
- * Returns the string representation of this source node along with a source
- * map.
- */
- SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
- var generated = {
- code: "",
- line: 1,
- column: 0
- };
- var map = new SourceMapGenerator(aArgs);
- var sourceMappingActive = false;
- this.walk(function (chunk, original) {
- generated.code += chunk;
- if (original.source !== null
- && original.line !== null
- && original.column !== null) {
- map.addMapping({
- source: original.source,
- original: {
- line: original.line,
- column: original.column
- },
- generated: {
- line: generated.line,
- column: generated.column
- },
- name: original.name
- });
- sourceMappingActive = true;
- } else if (sourceMappingActive) {
- map.addMapping({
- generated: {
- line: generated.line,
- column: generated.column
- }
- });
- sourceMappingActive = false;
- }
- chunk.split('').forEach(function (ch) {
- if (ch === '\n') {
- generated.line++;
- generated.column = 0;
- } else {
- generated.column++;
- }
- });
- });
- this.walkSourceContents(function (sourceFile, sourceContent) {
- map.setSourceContent(sourceFile, sourceContent);
- });
-
- return { code: generated.code, map: map };
- };
-
- exports.SourceNode = SourceNode;
-
-});
-
-},{"./source-map-generator":12,"./util":17,"amdefine":19}],21:[function(require,module,exports){
-// shim for using process in browser
-
-var process = module.exports = {};
-
-process.nextTick = (function () {
- var canSetImmediate = typeof window !== 'undefined'
- && window.setImmediate;
- var canPost = typeof window !== 'undefined'
- && window.postMessage && window.addEventListener
- ;
-
- if (canSetImmediate) {
- return function (f) { return window.setImmediate(f) };
- }
-
- if (canPost) {
- var queue = [];
- window.addEventListener('message', function (ev) {
- if (ev.source === window && ev.data === 'process-tick') {
- ev.stopPropagation();
- if (queue.length > 0) {
- var fn = queue.shift();
- fn();
- }
- }
- }, true);
-
- return function nextTick(fn) {
- queue.push(fn);
- window.postMessage('process-tick', '*');
- };
- }
-
- return function nextTick(fn) {
- setTimeout(fn, 0);
- };
-})();
-
-process.title = 'browser';
-process.browser = true;
-process.env = {};
-process.argv = [];
-
-process.binding = function (name) {
- throw new Error('process.binding is not supported');
-}
-
-// TODO(shtylman)
-process.cwd = function () { return '/' };
-process.chdir = function (dir) {
- throw new Error('process.chdir is not supported');
+/*global exports:true*/
+"use strict";
+var append = require('../lib/utils').append;
+var catchup = require('../lib/utils').catchup;
+var move = require('../lib/utils').move;
+var Syntax = require('esprima').Syntax;
+
+var knownTags = {
+ a: true,
+ abbr: true,
+ address: true,
+ applet: true,
+ area: true,
+ article: true,
+ aside: true,
+ audio: true,
+ b: true,
+ base: true,
+ bdi: true,
+ bdo: true,
+ big: true,
+ blockquote: true,
+ body: true,
+ br: true,
+ button: true,
+ canvas: true,
+ caption: true,
+ circle: true,
+ cite: true,
+ code: true,
+ col: true,
+ colgroup: true,
+ command: true,
+ data: true,
+ datalist: true,
+ dd: true,
+ del: true,
+ details: true,
+ dfn: true,
+ dialog: true,
+ div: true,
+ dl: true,
+ dt: true,
+ ellipse: true,
+ em: true,
+ embed: true,
+ fieldset: true,
+ figcaption: true,
+ figure: true,
+ footer: true,
+ form: true,
+ g: true,
+ h1: true,
+ h2: true,
+ h3: true,
+ h4: true,
+ h5: true,
+ h6: true,
+ head: true,
+ header: true,
+ hgroup: true,
+ hr: true,
+ html: true,
+ i: true,
+ iframe: true,
+ img: true,
+ input: true,
+ ins: true,
+ kbd: true,
+ keygen: true,
+ label: true,
+ legend: true,
+ li: true,
+ line: true,
+ link: true,
+ main: true,
+ map: true,
+ mark: true,
+ marquee: true,
+ menu: true,
+ menuitem: true,
+ meta: true,
+ meter: true,
+ nav: true,
+ noscript: true,
+ object: true,
+ ol: true,
+ optgroup: true,
+ option: true,
+ output: true,
+ p: true,
+ param: true,
+ path: true,
+ polyline: true,
+ pre: true,
+ progress: true,
+ q: true,
+ rect: true,
+ rp: true,
+ rt: true,
+ ruby: true,
+ s: true,
+ samp: true,
+ script: true,
+ section: true,
+ select: true,
+ small: true,
+ source: true,
+ span: true,
+ strong: true,
+ style: true,
+ sub: true,
+ summary: true,
+ sup: true,
+ svg: true,
+ table: true,
+ tbody: true,
+ td: true,
+ text: true,
+ textarea: true,
+ tfoot: true,
+ th: true,
+ thead: true,
+ time: true,
+ title: true,
+ tr: true,
+ track: true,
+ u: true,
+ ul: true,
+ 'var': true,
+ video: true,
+ wbr: true
};
-},{}],19:[function(require,module,exports){
-(function(process,__filename){/** vim: et:ts=4:sw=4:sts=4
- * @license amdefine 0.0.5 Copyright (c) 2011, The Dojo Foundation All Rights Reserved.
- * Available via the MIT or new BSD license.
- * see: http://github.com/jrburke/amdefine for details
- */
+function safeTrim(string) {
+ return string.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
+}
-/*jslint node: true */
-/*global module, process */
-'use strict';
-
-var path = require('path');
+// Replace all trailing whitespace characters with a single space character
+function trimWithSingleSpace(string) {
+ return string.replace(/^[ \t\xA0]{2,}/, ' ').
+ replace(/[ \t\xA0]{2,}$/, ' ').replace(/^\s+$/, '');
+}
/**
- * Creates a define for node.
- * @param {Object} module the "module" object that is defined by Node for the
- * current module.
- * @param {Function} [require]. Node's require function for the current module.
- * It only needs to be passed in Node versions before 0.5, when module.require
- * did not exist.
- * @returns {Function} a define function that is usable for the current node
- * module.
+ * Special handling for multiline string literals
+ * print lines:
+ *
+ * line
+ * line
+ *
+ * as:
+ *
+ * "line "+
+ * "line"
*/
-function amdefine(module, require) {
- var defineCache = {},
- loaderCache = {},
- alreadyCalled = false,
- makeRequire, stringRequire;
+function renderXJSLiteral(object, isLast, state, start, end) {
+ /** Added blank check filtering and triming*/
+ var trimmedChildValue = safeTrim(object.value);
+
+ if (trimmedChildValue) {
+ // head whitespace
+ append(object.value.match(/^[\t ]*/)[0], state);
+ if (start) {
+ append(start, state);
+ }
+
+ var trimmedChildValueWithSpace = trimWithSingleSpace(object.value);
/**
- * Trims the . and .. from an array of path segments.
- * It will keep a leading path segment if a .. will become
- * the first path segment, to help with module name lookups,
- * which act like paths, but can be remapped. But the end result,
- * all paths that use this function should look normalized.
- * NOTE: this method MODIFIES the input array.
- * @param {Array} ary the array of path segments.
*/
- function trimDots(ary) {
- var i, part;
- for (i = 0; ary[i]; i+= 1) {
- part = ary[i];
- if (part === '.') {
- ary.splice(i, 1);
- i -= 1;
- } else if (part === '..') {
- if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
- //End of the line. Keep at least one non-dot
- //path segment at the front so it can be mapped
- //correctly to disk. Otherwise, there is likely
- //no path mapping for a path starting with '..'.
- //This can still fail, but catches the most reasonable
- //uses of ..
- break;
- } else if (i > 0) {
- ary.splice(i - 1, 2);
- i -= 2;
- }
- }
- }
- }
+ var initialLines = trimmedChildValue.split(/\r\n|\n|\r/);
- function normalize(name, baseName) {
- var baseParts;
-
- //Adjust any relative paths.
- if (name && name.charAt(0) === '.') {
- //If have a base name, try to normalize against it,
- //otherwise, assume it is a top-level require that will
- //be relative to baseUrl in the end.
- if (baseName) {
- baseParts = baseName.split('/');
- baseParts = baseParts.slice(0, baseParts.length - 1);
- baseParts = baseParts.concat(name.split('/'));
- trimDots(baseParts);
- name = baseParts.join('/');
- }
- }
-
- return name;
- }
-
- /**
- * Create the normalize() function passed to a loader plugin's
- * normalize method.
- */
- function makeNormalize(relName) {
- return function (name) {
- return normalize(name, relName);
- };
- }
-
- function makeLoad(id) {
- function load(value) {
- loaderCache[id] = value;
- }
-
- load.fromText = function (id, text) {
- //This one is difficult because the text can/probably uses
- //define, and any relative paths and requires should be relative
- //to that id was it would be found on disk. But this would require
- //bootstrapping a module/require fairly deeply from node core.
- //Not sure how best to go about that yet.
- throw new Error('amdefine does not implement load.fromText');
- };
-
- return load;
- }
-
- makeRequire = function (systemRequire, exports, module, relId) {
- function amdRequire(deps, callback) {
- if (typeof deps === 'string') {
- //Synchronous, single module require('')
- return stringRequire(systemRequire, exports, module, deps, relId);
- } else {
- //Array of dependencies with a callback.
-
- //Convert the dependencies to modules.
- deps = deps.map(function (depName) {
- return stringRequire(systemRequire, exports, module, depName, relId);
- });
-
- //Wait for next tick to call back the require call.
- process.nextTick(function () {
- callback.apply(null, deps);
- });
- }
- }
-
- amdRequire.toUrl = function (filePath) {
- if (filePath.indexOf('.') === 0) {
- return normalize(filePath, path.dirname(module.filename));
- } else {
- return filePath;
- }
- };
-
- return amdRequire;
- };
-
- //Favor explicit value, passed in if the module wants to support Node 0.4.
- require = require || function req() {
- return module.require.apply(module, arguments);
- };
-
- function runFactory(id, deps, factory) {
- var r, e, m, result;
-
- if (id) {
- e = loaderCache[id] = {};
- m = {
- id: id,
- uri: __filename,
- exports: e
- };
- r = makeRequire(require, e, m, id);
- } else {
- //Only support one define call per file
- if (alreadyCalled) {
- throw new Error('amdefine with no module ID cannot be called more than once per file.');
- }
- alreadyCalled = true;
-
- //Use the real variables from node
- //Use module.exports for exports, since
- //the exports in here is amdefine exports.
- e = module.exports;
- m = module;
- r = makeRequire(require, e, m, module.id);
- }
-
- //If there are dependencies, they are strings, so need
- //to convert them to dependency values.
- if (deps) {
- deps = deps.map(function (depName) {
- return r(depName);
- });
- }
-
- //Call the factory with the right dependencies.
- if (typeof factory === 'function') {
- result = factory.apply(module.exports, deps);
- } else {
- result = factory;
- }
-
- if (result !== undefined) {
- m.exports = result;
- if (id) {
- loaderCache[id] = m.exports;
- }
- }
- }
-
- stringRequire = function (systemRequire, exports, module, id, relId) {
- //Split the ID by a ! so that
- var index = id.indexOf('!'),
- originalId = id,
- prefix, plugin;
-
- if (index === -1) {
- id = normalize(id, relId);
-
- //Straight module lookup. If it is one of the special dependencies,
- //deal with it, otherwise, delegate to node.
- if (id === 'require') {
- return makeRequire(systemRequire, exports, module, relId);
- } else if (id === 'exports') {
- return exports;
- } else if (id === 'module') {
- return module;
- } else if (loaderCache.hasOwnProperty(id)) {
- return loaderCache[id];
- } else if (defineCache[id]) {
- runFactory.apply(null, defineCache[id]);
- return loaderCache[id];
- } else {
- if(systemRequire) {
- return systemRequire(originalId);
- } else {
- throw new Error('No module with ID: ' + id);
- }
- }
- } else {
- //There is a plugin in play.
- prefix = id.substring(0, index);
- id = id.substring(index + 1, id.length);
-
- plugin = stringRequire(systemRequire, exports, module, prefix, relId);
-
- if (plugin.normalize) {
- id = plugin.normalize(id, makeNormalize(relId));
- } else {
- //Normalize the ID normally.
- id = normalize(id, relId);
- }
-
- if (loaderCache[id]) {
- return loaderCache[id];
- } else {
- plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
-
- return loaderCache[id];
- }
- }
- };
-
- //Create a define function specific to the module asking for amdefine.
- function define(id, deps, factory) {
- if (Array.isArray(id)) {
- factory = deps;
- deps = id;
- id = undefined;
- } else if (typeof id !== 'string') {
- factory = id;
- id = deps = undefined;
- }
-
- if (deps && !Array.isArray(deps)) {
- factory = deps;
- deps = undefined;
- }
-
- if (!deps) {
- deps = ['require', 'exports', 'module'];
- }
-
- //Set up properties for this module. If an ID, then use
- //internal cache. If no ID, then use the external variables
- //for this node module.
- if (id) {
- //Put the module in deep freeze until there is a
- //require call for it.
- defineCache[id] = [id, deps, factory];
- } else {
- runFactory(id, deps, factory);
- }
- }
-
- //define.require, which has access to all the values in the
- //cache. Useful for AMD modules that all have IDs in the file,
- //but need to finally export a value to node based on one of those
- //IDs.
- define.require = function (id) {
- if (loaderCache[id]) {
- return loaderCache[id];
- }
-
- if (defineCache[id]) {
- runFactory.apply(null, defineCache[id]);
- return loaderCache[id];
- }
- };
-
- define.amd = {};
-
- return define;
-}
-
-module.exports = amdefine;
-
-})(require("__browserify_process"),"/../node_modules/source-map/node_modules/amdefine/amdefine.js")
-},{"path":22,"__browserify_process":21}],16:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- *
- * Based on the Base 64 VLQ implementation in Closure Compiler:
- * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
- *
- * Copyright 2011 The Closure Compiler Authors. All rights reserved.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- var base64 = require('./base64');
-
- // A single base 64 digit can contain 6 bits of data. For the base 64 variable
- // length quantities we use in the source map spec, the first bit is the sign,
- // the next four bits are the actual value, and the 6th bit is the
- // continuation bit. The continuation bit tells us whether there are more
- // digits in this value following this digit.
- //
- // Continuation
- // | Sign
- // | |
- // V V
- // 101011
-
- var VLQ_BASE_SHIFT = 5;
-
- // binary: 100000
- var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
-
- // binary: 011111
- var VLQ_BASE_MASK = VLQ_BASE - 1;
-
- // binary: 100000
- var VLQ_CONTINUATION_BIT = VLQ_BASE;
-
- /**
- * Converts from a two-complement value to a value where the sign bit is
- * is placed in the least significant bit. For example, as decimals:
- * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
- * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
- */
- function toVLQSigned(aValue) {
- return aValue < 0
- ? ((-aValue) << 1) + 1
- : (aValue << 1) + 0;
- }
-
- /**
- * Converts to a two-complement value from a value where the sign bit is
- * is placed in the least significant bit. For example, as decimals:
- * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
- * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
- */
- function fromVLQSigned(aValue) {
- var isNegative = (aValue & 1) === 1;
- var shifted = aValue >> 1;
- return isNegative
- ? -shifted
- : shifted;
- }
-
- /**
- * Returns the base 64 VLQ encoded value.
- */
- exports.encode = function base64VLQ_encode(aValue) {
- var encoded = "";
- var digit;
-
- var vlq = toVLQSigned(aValue);
-
- do {
- digit = vlq & VLQ_BASE_MASK;
- vlq >>>= VLQ_BASE_SHIFT;
- if (vlq > 0) {
- // There are still more digits in this value, so we must make sure the
- // continuation bit is marked.
- digit |= VLQ_CONTINUATION_BIT;
- }
- encoded += base64.encode(digit);
- } while (vlq > 0);
-
- return encoded;
- };
-
- /**
- * Decodes the next base 64 VLQ value from the given string and returns the
- * value and the rest of the string.
- */
- exports.decode = function base64VLQ_decode(aStr) {
- var i = 0;
- var strLen = aStr.length;
- var result = 0;
- var shift = 0;
- var continuation, digit;
-
- do {
- if (i >= strLen) {
- throw new Error("Expected more digits in base 64 VLQ value.");
- }
- digit = base64.decode(aStr.charAt(i++));
- continuation = !!(digit & VLQ_CONTINUATION_BIT);
- digit &= VLQ_BASE_MASK;
- result = result + (digit << shift);
- shift += VLQ_BASE_SHIFT;
- } while (continuation);
-
- return {
- value: fromVLQSigned(result),
- rest: aStr.slice(i)
- };
- };
-
-});
-
-},{"./base64":23,"amdefine":19}],22:[function(require,module,exports){
-(function(process){function filter (xs, fn) {
- var res = [];
- for (var i = 0; i < xs.length; i++) {
- if (fn(xs[i], i, xs)) res.push(xs[i]);
- }
- return res;
-}
-
-// resolves . and .. elements in a path array with directory names there
-// must be no slashes, empty elements, or device names (c:\) in the array
-// (so also no leading and trailing slashes - it does not distinguish
-// relative and absolute paths)
-function normalizeArray(parts, allowAboveRoot) {
- // if the path tries to go above the root, `up` ends up > 0
- var up = 0;
- for (var i = parts.length; i >= 0; i--) {
- var last = parts[i];
- if (last == '.') {
- parts.splice(i, 1);
- } else if (last === '..') {
- parts.splice(i, 1);
- up++;
- } else if (up) {
- parts.splice(i, 1);
- up--;
- }
- }
-
- // if the path is allowed to go above the root, restore leading ..s
- if (allowAboveRoot) {
- for (; up--; up) {
- parts.unshift('..');
- }
- }
-
- return parts;
-}
-
-// Regex to split a filename into [*, dir, basename, ext]
-// posix version
-var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
-
-// path.resolve([from ...], to)
-// posix version
-exports.resolve = function() {
-var resolvedPath = '',
- resolvedAbsolute = false;
-
-for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
- var path = (i >= 0)
- ? arguments[i]
- : process.cwd();
-
- // Skip empty and invalid entries
- if (typeof path !== 'string' || !path) {
- continue;
- }
-
- resolvedPath = path + '/' + resolvedPath;
- resolvedAbsolute = path.charAt(0) === '/';
-}
-
-// At this point the path should be resolved to a full absolute path, but
-// handle relative paths to be safe (might happen when process.cwd() fails)
-
-// Normalize the path
-resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
- return !!p;
- }), !resolvedAbsolute).join('/');
-
- return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
-};
-
-// path.normalize(path)
-// posix version
-exports.normalize = function(path) {
-var isAbsolute = path.charAt(0) === '/',
- trailingSlash = path.slice(-1) === '/';
-
-// Normalize the path
-path = normalizeArray(filter(path.split('/'), function(p) {
- return !!p;
- }), !isAbsolute).join('/');
-
- if (!path && !isAbsolute) {
- path = '.';
- }
- if (path && trailingSlash) {
- path += '/';
- }
-
- return (isAbsolute ? '/' : '') + path;
-};
-
-
-// posix version
-exports.join = function() {
- var paths = Array.prototype.slice.call(arguments, 0);
- return exports.normalize(filter(paths, function(p, index) {
- return p && typeof p === 'string';
- }).join('/'));
-};
-
-
-exports.dirname = function(path) {
- var dir = splitPathRe.exec(path)[1] || '';
- var isWindows = false;
- if (!dir) {
- // No dirname
- return '.';
- } else if (dir.length === 1 ||
- (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
- // It is just a slash or a drive letter with a slash
- return dir;
- } else {
- // It is a full dirname, strip trailing slash
- return dir.substring(0, dir.length - 1);
- }
-};
-
-
-exports.basename = function(path, ext) {
- var f = splitPathRe.exec(path)[2] || '';
- // TODO: make this comparison case-insensitive on windows?
- if (ext && f.substr(-1 * ext.length) === ext) {
- f = f.substr(0, f.length - ext.length);
- }
- return f;
-};
-
-
-exports.extname = function(path) {
- return splitPathRe.exec(path)[3] || '';
-};
-
-exports.relative = function(from, to) {
- from = exports.resolve(from).substr(1);
- to = exports.resolve(to).substr(1);
-
- function trim(arr) {
- var start = 0;
- for (; start < arr.length; start++) {
- if (arr[start] !== '') break;
- }
-
- var end = arr.length - 1;
- for (; end >= 0; end--) {
- if (arr[end] !== '') break;
- }
-
- if (start > end) return [];
- return arr.slice(start, end - start + 1);
- }
-
- var fromParts = trim(from.split('/'));
- var toParts = trim(to.split('/'));
-
- var length = Math.min(fromParts.length, toParts.length);
- var samePartsLength = length;
- for (var i = 0; i < length; i++) {
- if (fromParts[i] !== toParts[i]) {
- samePartsLength = i;
- break;
- }
- }
-
- var outputParts = [];
- for (var i = samePartsLength; i < fromParts.length; i++) {
- outputParts.push('..');
- }
-
- outputParts = outputParts.concat(toParts.slice(samePartsLength));
-
- return outputParts.join('/');
-};
-
-})(require("__browserify_process"))
-},{"__browserify_process":21}],17:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- /**
- * This is a helper function for getting values from parameter/options
- * objects.
- *
- * @param args The object we are extracting values from
- * @param name The name of the property we are getting.
- * @param defaultValue An optional value to return if the property is missing
- * from the object. If this is not specified and the property is missing, an
- * error will be thrown.
- */
- function getArg(aArgs, aName, aDefaultValue) {
- if (aName in aArgs) {
- return aArgs[aName];
- } else if (arguments.length === 3) {
- return aDefaultValue;
- } else {
- throw new Error('"' + aName + '" is a required argument.');
- }
- }
- exports.getArg = getArg;
-
- var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
-
- function urlParse(aUrl) {
- var match = aUrl.match(urlRegexp);
- if (!match) {
- return null;
- }
- return {
- scheme: match[1],
- auth: match[3],
- host: match[4],
- port: match[6],
- path: match[7]
- };
- }
- exports.urlParse = urlParse;
-
- function urlGenerate(aParsedUrl) {
- var url = aParsedUrl.scheme + "://";
- if (aParsedUrl.auth) {
- url += aParsedUrl.auth + "@"
- }
- if (aParsedUrl.host) {
- url += aParsedUrl.host;
- }
- if (aParsedUrl.port) {
- url += ":" + aParsedUrl.port
- }
- if (aParsedUrl.path) {
- url += aParsedUrl.path;
- }
- return url;
- }
- exports.urlGenerate = urlGenerate;
-
- function join(aRoot, aPath) {
- var url;
-
- if (aPath.match(urlRegexp)) {
- return aPath;
- }
-
- if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
- url.path = aPath;
- return urlGenerate(url);
- }
-
- return aRoot.replace(/\/$/, '') + '/' + aPath;
- }
- exports.join = join;
-
- /**
- * Because behavior goes wacky when you set `__proto__` on objects, we
- * have to prefix all the strings in our set with an arbitrary character.
- *
- * See https://github.com/mozilla/source-map/pull/31 and
- * https://github.com/mozilla/source-map/issues/30
- *
- * @param String aStr
- */
- function toSetString(aStr) {
- return '$' + aStr;
- }
- exports.toSetString = toSetString;
-
- function fromSetString(aStr) {
- return aStr.substr(1);
- }
- exports.fromSetString = fromSetString;
-
- function relative(aRoot, aPath) {
- aRoot = aRoot.replace(/\/$/, '');
-
- var url = urlParse(aRoot);
- if (aPath.charAt(0) == "/" && url && url.path == "/") {
- return aPath.slice(1);
- }
-
- return aPath.indexOf(aRoot + '/') === 0
- ? aPath.substr(aRoot.length + 1)
- : aPath;
- }
- exports.relative = relative;
-
-});
-
-},{"amdefine":19}],18:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- var util = require('./util');
-
- /**
- * A data structure which is a combination of an array and a set. Adding a new
- * member is O(1), testing for membership is O(1), and finding the index of an
- * element is O(1). Removing elements from the set is not supported. Only
- * strings are supported for membership.
- */
- function ArraySet() {
- this._array = [];
- this._set = {};
- }
-
- /**
- * Static method for creating ArraySet instances from an existing array.
- */
- ArraySet.fromArray = function ArraySet_fromArray(aArray) {
- var set = new ArraySet();
- for (var i = 0, len = aArray.length; i < len; i++) {
- set.add(aArray[i]);
- }
- return set;
- };
-
- /**
- * Add the given string to this set.
- *
- * @param String aStr
- */
- ArraySet.prototype.add = function ArraySet_add(aStr) {
- if (this.has(aStr)) {
- // Already a member; nothing to do.
- return;
- }
- var idx = this._array.length;
- this._array.push(aStr);
- this._set[util.toSetString(aStr)] = idx;
- };
-
- /**
- * Is the given string a member of this set?
- *
- * @param String aStr
- */
- ArraySet.prototype.has = function ArraySet_has(aStr) {
- return Object.prototype.hasOwnProperty.call(this._set,
- util.toSetString(aStr));
- };
-
- /**
- * What is the index of the given string in the array?
- *
- * @param String aStr
- */
- ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
- if (this.has(aStr)) {
- return this._set[util.toSetString(aStr)];
- }
- throw new Error('"' + aStr + '" is not in the set.');
- };
-
- /**
- * What is the element at the given index?
- *
- * @param Number aIdx
- */
- ArraySet.prototype.at = function ArraySet_at(aIdx) {
- if (aIdx >= 0 && aIdx < this._array.length) {
- return this._array[aIdx];
- }
- throw new Error('No element indexed by ' + aIdx);
- };
-
- /**
- * Returns the array representation of this set (which has the proper indices
- * indicated by indexOf). Note that this is a copy of the internal array used
- * for storing the members so that no one can mess with internal state.
- */
- ArraySet.prototype.toArray = function ArraySet_toArray() {
- return this._array.slice();
- };
-
- exports.ArraySet = ArraySet;
-
-});
-
-},{"./util":17,"amdefine":19}],20:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- /**
- * Recursive implementation of binary search.
- *
- * @param aLow Indices here and lower do not contain the needle.
- * @param aHigh Indices here and higher do not contain the needle.
- * @param aNeedle The element being searched for.
- * @param aHaystack The non-empty array being searched.
- * @param aCompare Function which takes two elements and returns -1, 0, or 1.
- */
- function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
- // This function terminates when one of the following is true:
- //
- // 1. We find the exact element we are looking for.
- //
- // 2. We did not find the exact element, but we can return the next
- // closest element that is less than that element.
- //
- // 3. We did not find the exact element, and there is no next-closest
- // element which is less than the one we are searching for, so we
- // return null.
- var mid = Math.floor((aHigh - aLow) / 2) + aLow;
- var cmp = aCompare(aNeedle, aHaystack[mid]);
- if (cmp === 0) {
- // Found the element we are looking for.
- return aHaystack[mid];
- }
- else if (cmp > 0) {
- // aHaystack[mid] is greater than our needle.
- if (aHigh - mid > 1) {
- // The element is in the upper half.
- return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
- }
- // We did not find an exact match, return the next closest one
- // (termination case 2).
- return aHaystack[mid];
- }
- else {
- // aHaystack[mid] is less than our needle.
- if (mid - aLow > 1) {
- // The element is in the lower half.
- return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
- }
- // The exact needle element was not found in this haystack. Determine if
- // we are in termination case (2) or (3) and return the appropriate thing.
- return aLow < 0
- ? null
- : aHaystack[aLow];
- }
- }
-
- /**
- * This is an implementation of binary search which will always try and return
- * the next lowest value checked if there is no exact hit. This is because
- * mappings between original and generated line/col pairs are single points,
- * and there is an implicit region between each of them, so a miss just means
- * that you aren't on the very start of a region.
- *
- * @param aNeedle The element you are looking for.
- * @param aHaystack The array that is being searched.
- * @param aCompare A function which takes the needle and an element in the
- * array and returns -1, 0, or 1 depending on whether the needle is less
- * than, equal to, or greater than the element, respectively.
- */
- exports.search = function search(aNeedle, aHaystack, aCompare) {
- return aHaystack.length > 0
- ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
- : null;
- };
-
-});
-
-},{"amdefine":19}],23:[function(require,module,exports){
-/* -*- Mode: js; js-indent-level: 2; -*- */
-/*
- * Copyright 2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-if (typeof define !== 'function') {
- var define = require('amdefine')(module);
-}
-define(function (require, exports, module) {
-
- var charToIntMap = {};
- var intToCharMap = {};
-
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
- .split('')
- .forEach(function (ch, index) {
- charToIntMap[ch] = index;
- intToCharMap[index] = ch;
+ var lines = initialLines.filter(function(line) {
+ return safeTrim(line).length > 0;
});
- /**
- * Encode an integer in the range of 0 to 63 to a single base 64 digit.
- */
- exports.encode = function base64_encode(aNumber) {
- if (aNumber in intToCharMap) {
- return intToCharMap[aNumber];
+ var hasInitialNewLine = initialLines[0] !== lines[0];
+ var hasFinalNewLine =
+ initialLines[initialLines.length - 1] !== lines[lines.length - 1];
+
+ var numLines = lines.length;
+ lines.forEach(function (line, ii) {
+ var lastLine = ii === numLines - 1;
+ var trimmedLine = safeTrim(line);
+ if (trimmedLine === '' && !lastLine) {
+ append(line, state);
+ } else {
+ var preString = '';
+ var postString = '';
+ var leading = '';
+
+ if (ii === 0) {
+ if (hasInitialNewLine) {
+ preString = ' ';
+ leading = '\n';
+ }
+ if (trimmedChildValueWithSpace.substring(0, 1) === ' ') {
+ // If this is the first line, and the original content starts with
+ // whitespace, place a single space at the beginning.
+ preString = ' ';
+ }
+ } else {
+ leading = line.match(/^[ \t]*/)[0];
+ }
+ if (!lastLine || trimmedChildValueWithSpace.substr(
+ trimmedChildValueWithSpace.length - 1, 1) === ' ' ||
+ hasFinalNewLine
+ ) {
+ // If either not on the last line, or the original content ends with
+ // whitespace, place a single character at the end.
+ postString = ' ';
+ }
+
+ append(
+ leading +
+ JSON.stringify(
+ preString + trimmedLine + postString
+ ) +
+ (lastLine ? '' : '+') +
+ line.match(/[ \t]*$/)[0],
+ state);
+ }
+ if (!lastLine) {
+ append('\n', state);
+ }
+ });
+ } else {
+ if (start) {
+ append(start, state);
}
- throw new TypeError("Must be between 0 and 63: " + aNumber);
- };
+ append('""', state);
+ }
+ if (end) {
+ append(end, state);
+ }
- /**
- * Decode a single base 64 digit to an integer.
- */
- exports.decode = function base64_decode(aChar) {
- if (aChar in charToIntMap) {
- return charToIntMap[aChar];
+ // add comma before trailing whitespace
+ if (!isLast) {
+ append(',', state);
+ }
+
+ // tail whitespace
+ append(object.value.match(/[ \t]*$/)[0], state);
+ move(object.range[1], state);
+}
+
+function renderXJSExpressionContainer(traverse, object, isLast, path, state) {
+ // Plus 1 to skip `{`.
+ move(object.range[0] + 1, state);
+ traverse(object.expression, path, state);
+ if (!isLast && object.expression.type !== Syntax.XJSEmptyExpression) {
+ // If we need to append a comma, make sure to do so after the expression.
+ catchup(object.expression.range[1], state);
+ append(',', state);
+ }
+
+ // Minus 1 to skip `}`.
+ catchup(object.range[1] - 1, state);
+ move(object.range[1], state);
+ return false;
+}
+
+function quoteAttrName(attr) {
+ // Quote invalid JS identifiers.
+ if (!/^[a-z_$][a-z\d_$]*$/i.test(attr)) {
+ return "'" + attr + "'";
+ }
+ return attr;
+}
+
+exports.knownTags = knownTags;
+exports.renderXJSExpressionContainer = renderXJSExpressionContainer;
+exports.renderXJSLiteral = renderXJSLiteral;
+exports.quoteAttrName = quoteAttrName;
+
+})()
+},{"../lib/utils":18,"esprima":4}],23:[function(require,module,exports){
+(function(){/*global exports:true*/
+var classes = require('./transforms/classes');
+var react = require('./transforms/react');
+var reactDisplayName = require('./transforms/reactDisplayName');
+
+/**
+ * Map from transformName => orderedListOfVisitors.
+ */
+var transformVisitors = {
+ 'es6-classes': [
+ classes.visitClassExpression,
+ classes.visitClassDeclaration,
+ classes.visitSuperCall,
+ classes.visitPrivateProperty
+ ]
+};
+
+transformVisitors.react = transformVisitors[
+ "es6-classes"
+].concat([
+ react.visitReactTag,
+ reactDisplayName.visitReactDisplayName
+]);
+
+/**
+ * Specifies the order in which each transform should run.
+ */
+var transformRunOrder = [
+ 'es6-classes',
+ 'react'
+];
+
+/**
+ * Given a list of transform names, return the ordered list of visitors to be
+ * passed to the transform() function.
+ *
+ * @param {array?} excludes
+ * @return {array}
+ */
+function getVisitorsList(excludes) {
+ var ret = [];
+ for (var i = 0, il = transformRunOrder.length; i < il; i++) {
+ if (!excludes || excludes.indexOf(transformRunOrder[i]) === -1) {
+ ret = ret.concat(transformVisitors[transformRunOrder[i]]);
}
- throw new TypeError("Not a valid base 64 digit: " + aChar);
- };
+ }
+ return ret;
+}
-});
+exports.getVisitorsList = getVisitorsList;
+exports.transformVisitors = transformVisitors;
-},{"amdefine":19}]},{},[1])(1)
+})()
+},{"./transforms/classes":19,"./transforms/react":20,"./transforms/reactDisplayName":21}]},{},[15])(15)
});
;
\ No newline at end of file
diff --git a/js/examples/hello.js b/js/examples/hello.js
index 0e16593e1a..0097ffba55 100644
--- a/js/examples/hello.js
+++ b/js/examples/hello.js
@@ -14,6 +14,6 @@ React.renderComponent(, mountNode);\
";
React.renderComponent(
- ReactPlayground( {codeText:HELLO_COMPONENT}, null ),
+ ReactPlayground( {codeText:HELLO_COMPONENT} ),
document.getElementById('helloExample')
);
diff --git a/js/examples/markdown.js b/js/examples/markdown.js
index 53a6d8c5ff..7f26842a37 100644
--- a/js/examples/markdown.js
+++ b/js/examples/markdown.js
@@ -11,14 +11,14 @@ var MarkdownEditor = React.createClass({\n\
getInitialState: function() {\n\
return {value: 'Type some *markdown* here!'};\n\
},\n\
- handleKeyUp: React.autoBind(function() {\n\
+ handleInput: function() {\n\
this.setState({value: this.refs.textarea.getDOMNode().value});\n\
- }),\n\
+ },\n\
render: function() {\n\
return (\n\