UNPKG

23.5 kBMarkdownView Raw
1[![Build Status](https://travis-ci.org/Reactive-Extensions/RxJS.png)](https://travis-ci.org/Reactive-Extensions/RxJS)
2[![Inline docs](http://inch-ci.org/github/Reactive-Extensions/RxJS.svg?branch=master)](http://inch-ci.org/github/Reactive-Extensions/RxJS)
3[![GitHub version](http://img.shields.io/github/tag/reactive-extensions/rxjs.svg)](https://github.com/Reactive-Extensions/RxJS)
4[![NPM version](http://img.shields.io/npm/v/rx.svg)](https://npmjs.org/package/rx)
5[![Downloads](http://img.shields.io/npm/dm/rx.svg)](https://npmjs.org/package/rx)
6[![NuGet](http://img.shields.io/nuget/v/RxJS-All.svg)](http://www.nuget.org/packages/RxJS-All/)
7[![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)
8
9**[The Need to go Reactive](#the-need-to-go-reactive)** |
10**[About the Reactive Extensions](#about-the-reactive-extensions)** |
11**[Batteries Included](#batteries-included)** |
12**[Why RxJS?](#why-rxjs)** |
13**[Dive In!](#dive-in)** |
14**[Resources](#resources)** |
15**[Getting Started](#getting-started)** |
16**[What about my libraries?](#what-about-my-libraries)** |
17**[Compatibility](#compatibility)** |
18**[Contributing](#contributing)** |
19**[License](#license)**
20
21# The Reactive Extensions for JavaScript (RxJS) <sup>2.4</sup>... #
22*...is a set of libraries to compose asynchronous and event-based programs using observable collections and [Array#extras](http://blogs.msdn.com/b/ie/archive/2010/12/13/ecmascript-5-part-2-array-extras.aspx) style composition in JavaScript*
23
24The project is actively developed by [Microsoft Open Technologies, Inc.](http://msopentech.com/), in collaboration with a community of open source developers.
25
26## The Need to go Reactive ##
27
28Applications, especially on the web have changed over the years from being a simple static page, to DHTML with animations, to the Ajax revolution. Each time, we're adding more complexity, more data, and asynchronous behavior to our applications. How do we manage it all? How do we scale it? By moving towards "Reactive Architectures" which are event-driven, resilient and responsive. With the Reactive Extensions, you have all the tools you need to help build these systems.
29
30## About the Reactive Extensions ##
31
32The Reactive Extensions for JavaScript (RxJS) is a set of libraries for composing asynchronous and event-based programs using observable sequences and fluent query operators that many of you already know by [Array#extras](http://blogs.msdn.com/b/ie/archive/2010/12/13/ecmascript-5-part-2-array-extras.aspx) in JavaScript. Using RxJS, developers represent asynchronous data streams with Observables, query asynchronous data streams using our many operators, and parameterize the concurrency in the asynchronous data streams using Schedulers. Simply put, RxJS = Observables + Operators + Schedulers.
33
34Whether you are authoring a web-based application in JavaScript or a server-side application in Node.js, you have to deal with asynchronous and event-based programming as a matter of course. Although some patterns are emerging such as the Promise pattern, handling exceptions, cancellation, and synchronization is difficult and error-prone.
35
36Using RxJS, you can represent multiple asynchronous data streams (that come from diverse sources, e.g., stock quote, tweets, computer events, web service requests, etc.), and subscribe to the event stream using the Observer object. The Observable notifies the subscribed Observer instance whenever an event occurs.
37
38Because observable sequences are data streams, you can query them using standard query operators implemented by the Observable type. Thus you can filter, project, aggregate, compose and perform time-based operations on multiple events easily by using these operators. In addition, there are a number of other reactive stream specific operators that allow powerful queries to be written. Cancellation, exceptions, and synchronization are also handled gracefully by using the methods on the Observable object.
39
40But the best news of all is that you already know how to program like this. Take for example the following JavaScript code, where we get some stock data and then manipulate and then iterate the results.
41
42```js
43/* Get stock data somehow */
44var source = getStockData();
45
46source
47 .filter(function (quote) {
48 return quote.price > 30;
49 })
50 .map(function (quote) {
51 return quote.price;
52 })
53 .forEach(function (price) {
54 console.log('Prices higher than $30: $' + price);
55 });
56```
57
58Now what if this data were to come as some sort of event, for example a stream, such as as a WebSocket, then we could pretty much write the same query to iterate our data, with very little change.
59
60```js
61/* Get stock data somehow */
62var source = getAsyncStockData();
63
64var subscription = source
65 .filter(function (quote) {
66 return quote.price > 30;
67 })
68 .map(function (quote) {
69 return quote.price;
70 })
71 .forEach(
72 function (price) {
73 console.log('Prices higher than $30: $' + price);
74 },
75 function (err) {
76 console.log('Something went wrong: ' + err.message);
77 });
78
79/* When we're done */
80subscription.dispose();
81```
82
83The only difference is that we can handle the errors inline with our subscription. And when we're no longer interested in receiving the data as it comes streaming in, we call `dispose` on our subscription.
84
85## Batteries Included ##
86
87Sure, there are a lot of libraries to get started with RxJS? Confused on where to get started? Start out with the complete set of operators with [`rx.all.js`](doc/libraries/rx.complete.md), then you can reduce it to the number of operators that you really need, and perhaps stick with something as small as [`rx.lite.js`](doc/libraries/rx.lite.md).
88
89This set of libraries include:
90
91- [`rx.all.js`](doc/libraries/rx.complete.md) - complete version of RxJS with all operators, minus the testing operators, and comes with a compat file for older browsers.
92- [`rx.lite.js`](doc/libraries/rx.lite.md) - lite version with event bindings, creation, time and standard query operators with a compat file for older browsers. For most operations, this is the file you'll want to use unless you want the full power of RxJS.
93- [`rx.lite.extras.js`](doc/libraries/rx.lite.extras.md) - the operators missing from rx.lite.js that can be found in rx.js.
94- [`rx.js`](doc/libraries/rx.md) - core library for ES5 compliant browsers and runtimes plus compatibility for older browsers.
95- [`rx.aggregates.js`](doc/libraries/rx.aggregates.md) - aggregation event processing query operations
96- [`rx.async.js`](doc/libraries/rx.async.md) - async operations such as events, callbacks and promises plus a compat file for older browsers.
97- [`rx.backpressure.js`](doc/libraries/rx.backpressure.md) - backpressure operators such as pause/resume and controlled.
98- [`rx.binding.js`](doc/libraries/rx.binding.md) - binding operators including multicast, publish, publishLast, publishValue, and replay
99- [`rx.coincidence.js`](doc/libraries/rx.coincidence.md) - reactive coincidence join event processing query operations
100- [`rx.experimental.js`](doc/libraries/rx.experimental.md) - experimental operators including imperative operators and forkJoin
101- [`rx.joinpatterns.js`](doc/libraries/rx.joinpatterns.md) - join patterns event processing query operations
102- [`rx.testing.js`](doc/libraries/rx.testing.md) - used to write unit tests for complex event processing queries
103- [`rx.time.js`](doc/libraries/rx.time.md) - time-based event processing query operations
104- [`rx.virtualtime.js`](doc/libraries/rx.virtualtime.md) - virtual-time-based schedulers
105
106## Why RxJS? ##
107
108One question you may ask yourself, is why RxJS? What about Promises? Promises are good for solving asynchronous operations such as querying a service with an XMLHttpRequest, where the expected behavior is one value and then completion. The Reactive Extensions for JavaScript unifies both the world of Promises, callbacks as well as evented data such as DOM Input, Web Workers, Web Sockets. Once we have unified these concepts, this enables rich composition.
109
110To give you an idea about rich composition, we can create an autocompletion service which takes the user input from a text input and then query a service, making sure not to flood the service with calls for every key stroke, but instead allow to go at a more natural pace.
111
112First, we'll reference the JavaScript files, including jQuery, although RxJS has no dependencies on jQuery...
113```html
114<script src="http://code.jquery.com/jquery.js"></script>
115<script src="rx.lite.js"></script>
116```
117Next, we'll get the user input from an input, listening to the keyup event by using the `Rx.Observable.fromEvent` method. This will either use the event binding from [jQuery](http://jquery.com), [Zepto](http://zeptojs.com/), [AngularJS](https://angularjs.org/), [Backbone.js](http://backbonejs.org/) and [Ember.js](http://emberjs.com/) if available, and if not, falls back to the native event binding. This gives you consistent ways of thinking of events depending on your framework, so there are no surprises.
118
119```js
120var $input = $('#input'),
121 $results = $('#results');
122
123/* Only get the value from each key up */
124var keyups = Rx.Observable.fromEvent($input, 'keyup')
125 .map(function (e) {
126 return e.target.value;
127 })
128 .filter(function (text) {
129 return text.length > 2;
130 });
131
132/* Now debounce the input for 500ms */
133var debounced = keyups
134 .debounce(500 /* ms */);
135
136/* Now get only distinct values, so we eliminate the arrows and other control characters */
137var distinct = debounced
138 .distinctUntilChanged();
139```
140
141Now, let's query Wikipedia! In RxJS, we can instantly bind to any [Promises A+](https://github.com/promises-aplus/promises-spec) implementation through the `Rx.Observable.fromPromise` method or by just directly returning it, and we wrap it for you.
142
143```js
144function searchWikipedia (term) {
145 return $.ajax({
146 url: 'http://en.wikipedia.org/w/api.php',
147 dataType: 'jsonp',
148 data: {
149 action: 'opensearch',
150 format: 'json',
151 search: term
152 }
153 }).promise();
154}
155```
156
157Once that is created, now we can tie together the distinct throttled input and then query the service. In this case, we'll call `flatMapLatest` to get the value and ensure that we're not introducing any out of order sequence calls.
158
159```js
160var suggestions = distinct
161 .flatMapLatest(searchWikipedia);
162```
163
164Finally, we call the `forEach` method on our observable sequence to start pulling data.
165
166```js
167suggestions.forEach(
168 function (data) {
169 $results
170 .empty()
171 .append ($.map(data[1], function (value) {
172 return $('<li>').text(value);
173 }));
174 },
175 function (error) {
176 $results
177 .empty()
178 .append($('<li>'))
179 .text('Error:' + error);
180 });
181```
182
183And there you have it!
184
185## Dive In! ##
186
187Please check out:
188
189 - [The full documentation](https://github.com/Reactive-Extensions/RxJS/tree/master/doc)
190 - [Our many great examples](https://github.com/Reactive-Extensions/RxJS/tree/master/examples)
191 - [Our design guidelines](https://github.com/Reactive-Extensions/RxJS/tree/master/doc/designguidelines)
192 - [Our contribution guidelines](https://github.com/Reactive-Extensions/RxJS/tree/master/doc/contributing)
193 - [Our complete Unit Tests](https://github.com/Reactive-Extensions/RxJS/tree/master/tests)
194
195## Resources
196
197- Contact us
198 - [Tech Blog](http://blogs.msdn.com/b/rxteam)
199 - [Twitter @ReactiveX](https://twitter.com/ReactiveX)
200 - [Twitter @OpenAtMicrosoft](http://twitter.com/OpenAtMicrosoft)
201 - [IRC #reactivex](http://webchat.freenode.net/#reactivex)
202 - [JabbR rx](https://jabbr.net/#/rooms/rx)
203 - [StackOverflow rxjs](http://stackoverflow.com/questions/tagged/rxjs)
204 - [Google Group rxjs](https://groups.google.com/forum/#!forum/rxjs)
205
206- Tutorials
207 - [The introduction to Reactive Programming you've been missing](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754)
208 - [2 minute introduction to Rx](https://medium.com/@andrestaltz/2-minute-introduction-to-rx-24c8ca793877)
209 - [Learn RxJS](https://github.com/jhusain/learnrx)
210 - [RxJS Koans](https://github.com/Reactive-Extensions/RxJSKoans)
211 - [RxJS Workshop](https://github.com/Reactive-Extensions/BuildStuffWorkshop)
212 - [Rx Workshop](http://rxworkshop.codeplex.com/)
213 - [Reactive Programming and MVC](http://aaronstacy.com/writings/reactive-programming-and-mvc/)
214
215- Reference Material
216 - [Rx Marbles](http://rxmarbles.com/)
217 - [RxJS GitBook](http://xgrommx.github.io/rx-book/)
218 - [Intro to Rx](http://introtorx.com/)
219 - [101 Rx Samples Wiki](http://rxwiki.wikidot.com/101samples)
220 - [Rx Design Guidelines](https://github.com/Reactive-Extensions/RxJS/tree/master/doc/designguidelines)
221 - [Beginners Guide to Rx](http://msdn.microsoft.com/en-us/data/gg577611)
222
223- Community Examples
224 - [React](http://facebook.github.io/react/)
225 - [Rx-React](https://github.com/fdecampredon/rx-react)
226 - [RxReact](https://github.com/AlexMost/RxReact)
227 - [React RxJS Autocomplete](https://github.com/eliseumds/react-autocomplete)
228 - [React RxJS TODO MVC](https://github.com/fdecampredon/react-rxjs-todomvc)
229 - [Rx TODO MVC](https://github.com/footballradar/rx-todomvc)
230 - [React RxJS Router](https://github.com/kmcclosk/reactjs-rxjs-example)
231 - [React + RxJS + Angular 2.0 di.js TODO MVC](https://github.com/joelhooks/react-rxjs-angular-di-todomvc)
232 - [React + RxJS Reactive Cube](https://github.com/hugobessaa/cubactive)
233 - [Real-Time with React + RxJS + Meteor](https://medium.com/@bobiblazeski/functional-reactive-interfaces-e8de034de6bd)
234 - [Flux](http://facebook.github.io/flux/)
235 - [Rx-Flux](https://github.com/fdecampredon/rx-flux)
236 - [ReactiveFlux](https://github.com/codesuki/reactive-flux)
237 - [Thundercats.js](https://github.com/r3dm/thundercats)
238 - [Ember](http://emberjs.com/)
239 - [RxEmber](https://github.com/blesh/RxEmber)
240 - [AngularJS](http://angularjs.org)
241 - [Ninya.io - Angular + RxJS + rx.angular.js](https://github.com/ninya-io/ninya.io) - [Site](http://stackwho.herokuapp.com/)
242 - [The Car Game](https://github.com/mikkark/TheCarGame) - [Site](https://thecargame.herokuapp.com/) - [Blog Post](https://allthingsarchitectural.wordpress.com/2014/11/24/game-programming-with-angular-rxjs-and-node-js/)
243 - [Cycle](https://github.com/staltz/cycle)
244 - [Cycle TODO MVC](https://github.com/staltz/todomvc-cycle)
245 - Everything else
246 - [Mario Elm Example](http://fudini.github.io/rx/mario.html)
247 - [Firebase + RxJS](http://blog.cryptoguru.com/2014/11/frp-using-rxjs-and-firebase.html)
248 - [Reactive Trader](https://github.com/AdaptiveConsulting/ReactiveTrader) - [Site](https://reactivetrader.azurewebsites.net/)
249
250- Presentations
251 - Don't Cross the Streams - Cascadia.js 2012 [slides/demos](http://www.slideshare.net/mattpodwysocki/cascadiajs-dont-cross-the-streams) | [video](http://www.youtube.com/watch?v=FqBq4uoiG0M)
252 - Curing Your Asynchronous Blues - Strange Loop 2013 [slides/demos](https://github.com/Reactive-Extensions/StrangeLoop2013) | [video](http://www.infoq.com/presentations/rx-event-processing)
253 - Streaming and event-based programming using FRP and RxJS - FutureJS 2014 [slides/demos](https://github.com/Reactive-Extensions/FutureJS) | [video](https://www.youtube.com/watch?v=zlERo_JMGCw)
254 - [Tyrannosaurus Rx](http://yobriefca.se/presentations/tyrannosaurus-rx.pdf) - [James Hughes](http://twitter.com/kouphax)
255 - Taming Asynchronous Workflows with Functional Reactive Programming - EuroClojure - [Leonardo Borges](https://twitter.com/leonardo_borges) [slides](http://www.slideshare.net/borgesleonardo/functional-reactive-programming-compositional-event-systems) | [video](http://www.slideshare.net/borgesleonardo/functional-reactive-programming-compositional-event-systems)
256- Reactive All the Things - ng-conf 2015 - [Martin Gontovnikas](https://twitter.com/mgonto/) & [Ben Lesh](https://twitter.com/BenLesh)
257 - [Slides](http://mgonto.github.io/reactive-all-the-things-talk/#1)
258 - [Video](https://www.youtube.com/watch?v=zbBVG8bOoXk&feature=youtu.be&app=desktop)
259
260- Videos
261 - [Practical Rx with Matthew Podwysocki, Bart de Smet and Jafar Husain](http://channel9.msdn.com/posts/Bart-De-Smet-Jafar-Hussain-Matthew-Podwysocki-Pragmatic-Rx)
262 - [Netflix and RxJS](http://channel9.msdn.com/posts/Rx-and-Netflix)
263 - [Hello RxJS - Channel 9](http://channel9.msdn.com/Blogs/Charles/Introducing-RxJS-Reactive-Extensions-for-JavaScript)
264 - [MIX 2011](http://channel9.msdn.com/events/MIX/MIX11/HTM07)
265 - [RxJS Today and Tomorrow - Channel 9](http://channel9.msdn.com/Blogs/Charles/Matthew-Podwysocki-and-Bart-J-F-De-Smet-RxJS-Today-and-Tomorrow)
266 - [Reactive Extensions Videos on Channel 9](http://channel9.msdn.com/Tags/reactive+extensions)
267 - [Asynchronous JavaScript at Netflix - Netflix JavaScript Talks - Jafar Husain](https://www.youtube.com/watch?v=XRYN2xt11Ek)
268 - [Asynchronous JavaScript at Netflix - MountainWest JavaScript 2014 - Jafar Husain](https://www.youtube.com/watch?v=XE692Clb5LU)
269 - [Asynchronous JavaScript at Netflix - HTML5DevConf - Jafar Husain](https://www.youtube.com/watch?v=5uxSu-F5Kj0)
270 - [Adding Even More Fun to Functional Programming With RXJS - Ryan Anklam](https://www.youtube.com/watch?v=8EExNfm0gt4)
271 - [Reactive Angular - Devoxx France 2014 - Martin Gontovnikas](http://parleys.com/play/53677646e4b0593229b85841/chapter0/about)
272 - [Reactive Game Programming for the Discerning Hipster - JSConf 2014 - Bodil Stokke](https://www.youtube.com/watch?v=x8mmAu7ZR9Y)
273
274- Podcasts
275 - [.NET Rocks #907](http://dotnetrocks.com/default.aspx?showNum=907)
276 - [JavaScript Jabber #83](http://javascriptjabber.com/083-jsj-frp-and-rxjs-with-matthew-podwysocki/)
277
278- Articles
279 - [Your Mouse is a Database](http://queue.acm.org/detail.cfm?id=2169076)
280
281- Books
282 - [RxJS](http://xgrommx.github.io/rx-book)
283 - [Intro to Rx](http://www.amazon.com/Introduction-to-Rx-ebook/dp/B008GM3YPM/)
284 - [Programming Reactive Extensions and LINQ](http://www.amazon.com/Programming-Reactive-Extensions-Jesse-Liberty/dp/1430237473/)
285
286## Getting Started
287
288There are a number of ways to get started with RxJS. The files are available on [cdnjs](http://cdnjs.com/libraries/rxjs/) and [jsDelivr](http://www.jsdelivr.com/#!rxjs).
289
290### Custom Builds
291
292You can use the [`rx-cli`](https://www.npmjs.org/package/rx-cli) to perform custom builds to create the RxJS you want:
293
294```bash
295$ rx --lite --compat --methods select,selectmany,takeuntil,fromevent
296```
297
298### Download the Source
299
300```bash
301git clone https://github.com/Reactive-Extensions/rxjs.git
302cd ./rxjs
303```
304
305### Installing with [NPM](https://npmjs.org/)
306
307```bash`
308$ npm install rx
309$ npm install -g rx
310```
311
312### Using with Node.js and Ringo.js
313
314```js
315var Rx = require('rx');
316```
317
318### Installing with [Bower](http://bower.io/)
319
320```bash
321$ bower install rxjs
322```
323
324### Installing with [Jam](http://jamjs.org/)
325```bash
326$ jam install rx
327```
328### Installing All of RxJS via [NuGet](http://nuget.org/)
329```bash
330$ Install-Package RxJS-All
331```
332### Install individual packages via [NuGet](http://nuget.org/):
333
334 Install-Package RxJS-All
335 Install-Package RxJS-Lite
336 Install-Package RxJS-Main
337 Install-Package RxJS-Aggregates
338 Install-Package RxJS-Async
339 Install-Package RxJS-BackPressure
340 Install-Package RxJS-Binding
341 Install-Package RxJS-Coincidence
342 Install-Package RxJS-Experimental
343 Install-Package RxJS-JoinPatterns
344 Install-Package RxJS-Testing
345 Install-Package RxJS-Time
346
347### In a Browser:
348
349```html
350<!-- Just the core RxJS -->
351<script src="rx.js"></script>
352
353<!-- Or all of RxJS minus testing -->
354<script src="rx.all.js"></script>
355
356<!-- Or keeping it lite -->
357<script src="rx.lite.js"></script>
358```
359
360### Along with a number of our extras for RxJS:
361
362```html
363<script src="rx.aggregates.js"></script>
364<script src="rx.async.js"></script>
365<script src="rx.backpressure.js"></script>
366<script src="rx.binding.js"></script>
367<script src="rx.coincidencejs"></script>
368<script src="rx.experimental.js"></script>
369<script src="rx.joinpatterns.js"></script>
370<script src="rx.time.js"></script>
371<script src="rx.virtualtime.js"></script>
372<script src="rx.testing.js"></script>
373```
374
375### Using RxJS with an AMD loader such as Require.js
376
377```js
378require({
379 'paths': {
380 'rx': 'path/to/rx-lite.js'
381 }
382},
383['rx'], function(Rx) {
384 var obs = Rx.Observable.of(42);
385 obs.forEach(function (x) { console.log(x); });
386});
387```
388
389## What about my libraries? ##
390
391The Reactive Extensions for JavaScript have no external dependencies any library, so they'll work well with just about any library. We provide bridges and support for various libraries including:
392
393- [React](http://facebook.github.io/react/)
394 - [Rx-React](https://github.com/fdecampredon/rx-react)
395 - [RxReact](https://github.com/AlexMost/RxReact)
396- [Flux](http://facebook.github.io/flux/)
397 - [Rx-Flux](https://github.com/fdecampredon/rx-flux)
398 - [ReactiveFlux](https://github.com/codesuki/reactive-flux)
399 - [Thundercats.js](https://github.com/r3dm/thundercats)
400- [Ember](http://emberjs.com/)
401 - [RxEmber](https://github.com/blesh/RxEmber)
402- [AngularJS](https://github.com/Reactive-Extensions/rx.angular.js)
403- [HTML DOM](https://github.com/Reactive-Extensions/RxJS-DOM)
404- [jQuery (1.4+)](https://github.com/Reactive-Extensions/RxJS-jQuery)
405- [MooTools](https://github.com/Reactive-Extensions/RxJS-MooTools)
406- [Dojo 1.7+](https://github.com/Reactive-Extensions/RxJS-Dojo)
407- [ExtJS](https://github.com/Reactive-Extensions/RxJS-ExtJS)
408
409In addition, we have support for [common Node.js functions](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/nodejs/nodejs.md) such as binding to callbacks and the `EventEmitter` class.
410
411## Compatibility ##
412
413RxJS has been thoroughly tested against all major browsers and supports IE6+, Chrome 4+, FireFox 1+, and Node.js v0.4+.
414
415## Contributing ##
416
417There are lots of ways to contribute to the project, and we appreciate our [contributors](https://github.com/Reactive-Extensions/RxJS/wiki/Contributors). If you wish to contribute, check out our [style guide]((https://github.com/Reactive-Extensions/RxJS/tree/master/doc/contributing)).
418
419You can contribute by reviewing and sending feedback on code checkins, suggesting and trying out new features as they are implemented, submit bugs and help us verify fixes as they are checked in, as well as submit code fixes or code contributions of your own. Note that all code submissions will be rigorously reviewed and tested by the Rx Team, and only those that meet an extremely high bar for both quality and design/roadmap appropriateness will be merged into the source.
420
421## License ##
422
423Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
424Microsoft Open Technologies would like to thank its contributors, a list
425of whom are at https://github.com/Reactive-Extensions/RxJS/wiki/Contributors.
426
427Licensed under the Apache License, Version 2.0 (the "License"); you
428may not use this file except in compliance with the License. You may
429obtain a copy of the License at
430
431http://www.apache.org/licenses/LICENSE-2.0
432
433Unless required by applicable law or agreed to in writing, software
434distributed under the License is distributed on an "AS IS" BASIS,
435WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
436implied. See the License for the specific language governing permissions
437and limitations under the License.